blob: 03a13843cd1624a2e6b89e506f18dd9037ba95bc [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 Hedberg4b34ee782012-02-21 14:13:02 +0200121#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
122 !test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
123
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200124struct pending_cmd {
125 struct list_head list;
Johan Hedbergfc2f4b12011-11-09 13:58:56 +0200126 u16 opcode;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200127 int index;
Szymon Jancc68fb7f2011-03-22 13:12:19 +0100128 void *param;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200129 struct sock *sk;
Johan Hedberge9a416b2011-02-19 12:05:56 -0300130 void *user_data;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200131};
132
Johan Hedbergca69b792011-11-11 18:10:00 +0200133/* HCI to MGMT error code conversion table */
134static u8 mgmt_status_table[] = {
135 MGMT_STATUS_SUCCESS,
136 MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */
137 MGMT_STATUS_NOT_CONNECTED, /* No Connection */
138 MGMT_STATUS_FAILED, /* Hardware Failure */
139 MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */
140 MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */
141 MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */
142 MGMT_STATUS_NO_RESOURCES, /* Memory Full */
143 MGMT_STATUS_TIMEOUT, /* Connection Timeout */
144 MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */
145 MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */
146 MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */
147 MGMT_STATUS_BUSY, /* Command Disallowed */
148 MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */
149 MGMT_STATUS_REJECTED, /* Rejected Security */
150 MGMT_STATUS_REJECTED, /* Rejected Personal */
151 MGMT_STATUS_TIMEOUT, /* Host Timeout */
152 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */
153 MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */
154 MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */
155 MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */
156 MGMT_STATUS_DISCONNECTED, /* OE Power Off */
157 MGMT_STATUS_DISCONNECTED, /* Connection Terminated */
158 MGMT_STATUS_BUSY, /* Repeated Attempts */
159 MGMT_STATUS_REJECTED, /* Pairing Not Allowed */
160 MGMT_STATUS_FAILED, /* Unknown LMP PDU */
161 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */
162 MGMT_STATUS_REJECTED, /* SCO Offset Rejected */
163 MGMT_STATUS_REJECTED, /* SCO Interval Rejected */
164 MGMT_STATUS_REJECTED, /* Air Mode Rejected */
165 MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */
166 MGMT_STATUS_FAILED, /* Unspecified Error */
167 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */
168 MGMT_STATUS_FAILED, /* Role Change Not Allowed */
169 MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */
170 MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */
171 MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */
172 MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */
173 MGMT_STATUS_FAILED, /* Unit Link Key Used */
174 MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */
175 MGMT_STATUS_TIMEOUT, /* Instant Passed */
176 MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */
177 MGMT_STATUS_FAILED, /* Transaction Collision */
178 MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */
179 MGMT_STATUS_REJECTED, /* QoS Rejected */
180 MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */
181 MGMT_STATUS_REJECTED, /* Insufficient Security */
182 MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */
183 MGMT_STATUS_BUSY, /* Role Switch Pending */
184 MGMT_STATUS_FAILED, /* Slot Violation */
185 MGMT_STATUS_FAILED, /* Role Switch Failed */
186 MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */
187 MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */
188 MGMT_STATUS_BUSY, /* Host Busy Pairing */
189 MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */
190 MGMT_STATUS_BUSY, /* Controller Busy */
191 MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */
192 MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */
193 MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */
194 MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */
195 MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */
196};
197
198static u8 mgmt_status(u8 hci_status)
199{
200 if (hci_status < ARRAY_SIZE(mgmt_status_table))
201 return mgmt_status_table[hci_status];
202
203 return MGMT_STATUS_FAILED;
204}
205
Szymon Janc4e51eae2011-02-25 19:05:48 +0100206static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200207{
208 struct sk_buff *skb;
209 struct mgmt_hdr *hdr;
210 struct mgmt_ev_cmd_status *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300211 int err;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200212
Szymon Janc34eb5252011-02-28 14:10:08 +0100213 BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200214
215 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
216 if (!skb)
217 return -ENOMEM;
218
219 hdr = (void *) skb_put(skb, sizeof(*hdr));
220
221 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100222 hdr->index = cpu_to_le16(index);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200223 hdr->len = cpu_to_le16(sizeof(*ev));
224
225 ev = (void *) skb_put(skb, sizeof(*ev));
226 ev->status = status;
227 put_unaligned_le16(cmd, &ev->opcode);
228
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300229 err = sock_queue_rcv_skb(sk, skb);
230 if (err < 0)
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200231 kfree_skb(skb);
232
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300233 return err;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200234}
235
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200236static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
237 void *rp, size_t rp_len)
Johan Hedberg02d98122010-12-13 21:07:04 +0200238{
239 struct sk_buff *skb;
240 struct mgmt_hdr *hdr;
241 struct mgmt_ev_cmd_complete *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300242 int err;
Johan Hedberg02d98122010-12-13 21:07:04 +0200243
244 BT_DBG("sock %p", sk);
245
Johan Hedberga38528f2011-01-22 06:46:43 +0200246 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC);
Johan Hedberg02d98122010-12-13 21:07:04 +0200247 if (!skb)
248 return -ENOMEM;
249
250 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedberga38528f2011-01-22 06:46:43 +0200251
Johan Hedberg02d98122010-12-13 21:07:04 +0200252 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100253 hdr->index = cpu_to_le16(index);
Johan Hedberga38528f2011-01-22 06:46:43 +0200254 hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200255
Johan Hedberga38528f2011-01-22 06:46:43 +0200256 ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
257 put_unaligned_le16(cmd, &ev->opcode);
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200258 ev->status = status;
Szymon Janc8020c162011-02-28 14:09:50 +0100259
260 if (rp)
261 memcpy(ev->data, rp, rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200262
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300263 err = sock_queue_rcv_skb(sk, skb);
264 if (err < 0)
Johan Hedberg02d98122010-12-13 21:07:04 +0200265 kfree_skb(skb);
266
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300267 return err;;
Johan Hedberg02d98122010-12-13 21:07:04 +0200268}
269
Johan Hedberga38528f2011-01-22 06:46:43 +0200270static int read_version(struct sock *sk)
271{
272 struct mgmt_rp_read_version rp;
273
274 BT_DBG("sock %p", sk);
275
276 rp.version = MGMT_VERSION;
277 put_unaligned_le16(MGMT_REVISION, &rp.revision);
278
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200279 return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +0100280 sizeof(rp));
Johan Hedberga38528f2011-01-22 06:46:43 +0200281}
282
Johan Hedberge70bb2e2012-02-13 16:59:33 +0200283static int read_commands(struct sock *sk)
284{
285 struct mgmt_rp_read_commands *rp;
286 u16 num_commands = ARRAY_SIZE(mgmt_commands);
287 u16 num_events = ARRAY_SIZE(mgmt_events);
288 u16 *opcode;
289 size_t rp_size;
290 int i, err;
291
292 BT_DBG("sock %p", sk);
293
294 rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16));
295
296 rp = kmalloc(rp_size, GFP_KERNEL);
297 if (!rp)
298 return -ENOMEM;
299
300 put_unaligned_le16(num_commands, &rp->num_commands);
301 put_unaligned_le16(num_events, &rp->num_events);
302
303 for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++)
304 put_unaligned_le16(mgmt_commands[i], opcode);
305
306 for (i = 0; i < num_events; i++, opcode++)
307 put_unaligned_le16(mgmt_events[i], opcode);
308
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200309 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp,
Johan Hedberge70bb2e2012-02-13 16:59:33 +0200310 rp_size);
311 kfree(rp);
312
313 return err;
314}
315
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200316static int read_index_list(struct sock *sk)
317{
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200318 struct mgmt_rp_read_index_list *rp;
319 struct list_head *p;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200320 struct hci_dev *d;
Johan Hedberga38528f2011-01-22 06:46:43 +0200321 size_t rp_len;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200322 u16 count;
Johan Hedberga38528f2011-01-22 06:46:43 +0200323 int i, err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200324
325 BT_DBG("sock %p", sk);
326
327 read_lock(&hci_dev_list_lock);
328
329 count = 0;
330 list_for_each(p, &hci_dev_list) {
331 count++;
332 }
333
Johan Hedberga38528f2011-01-22 06:46:43 +0200334 rp_len = sizeof(*rp) + (2 * count);
335 rp = kmalloc(rp_len, GFP_ATOMIC);
336 if (!rp) {
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100337 read_unlock(&hci_dev_list_lock);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200338 return -ENOMEM;
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100339 }
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200340
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200341 put_unaligned_le16(count, &rp->num_controllers);
342
343 i = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200344 list_for_each_entry(d, &hci_dev_list, list) {
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 Hedbergaee9b2182012-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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
Marcel Holtmannf0d4b782012-02-21 12:14:25 +0100395 if (!test_bit(HCI_UP, &hdev->flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200396 return settings;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200397
Marcel Holtmannf0d4b782012-02-21 12:14:25 +0100398 if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
399 settings |= MGMT_SETTING_POWERED;
400
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200401 if (test_bit(HCI_PSCAN, &hdev->flags))
402 settings |= MGMT_SETTING_CONNECTABLE;
403
404 if (test_bit(HCI_ISCAN, &hdev->flags))
405 settings |= MGMT_SETTING_DISCOVERABLE;
406
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200407 if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200408 settings |= MGMT_SETTING_PAIRABLE;
409
410 if (!(hdev->features[4] & LMP_NO_BREDR))
411 settings |= MGMT_SETTING_BREDR;
412
Andre Guedes59e29402011-12-30 10:34:03 -0300413 if (hdev->host_features[0] & LMP_HOST_LE)
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200414 settings |= MGMT_SETTING_LE;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200415
416 if (test_bit(HCI_AUTH, &hdev->flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200417 settings |= MGMT_SETTING_LINK_SECURITY;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200418
Johan Hedberg84bde9d2012-01-25 14:21:06 +0200419 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200420 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200421
Johan Hedberg6d80dfd2012-02-20 23:50:38 +0200422 if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
423 settings |= MGMT_SETTING_HS;
424
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200425 return settings;
Johan Hedbergc542a062011-01-26 13:11:03 +0200426}
427
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300428#define PNP_INFO_SVCLASS_ID 0x1200
429
430static u8 bluetooth_base_uuid[] = {
431 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
432 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
433};
434
435static u16 get_uuid16(u8 *uuid128)
436{
437 u32 val;
438 int i;
439
440 for (i = 0; i < 12; i++) {
441 if (bluetooth_base_uuid[i] != uuid128[i])
442 return 0;
443 }
444
445 memcpy(&val, &uuid128[12], 4);
446
447 val = le32_to_cpu(val);
448 if (val > 0xffff)
449 return 0;
450
451 return (u16) val;
452}
453
454static void create_eir(struct hci_dev *hdev, u8 *data)
455{
456 u8 *ptr = data;
457 u16 eir_len = 0;
458 u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
459 int i, truncated = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200460 struct bt_uuid *uuid;
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300461 size_t name_len;
462
463 name_len = strlen(hdev->dev_name);
464
465 if (name_len > 0) {
466 /* EIR Data type */
467 if (name_len > 48) {
468 name_len = 48;
469 ptr[1] = EIR_NAME_SHORT;
470 } else
471 ptr[1] = EIR_NAME_COMPLETE;
472
473 /* EIR Data length */
474 ptr[0] = name_len + 1;
475
476 memcpy(ptr + 2, hdev->dev_name, name_len);
477
478 eir_len += (name_len + 2);
479 ptr += (name_len + 2);
480 }
481
482 memset(uuid16_list, 0, sizeof(uuid16_list));
483
484 /* Group all UUID16 types */
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200485 list_for_each_entry(uuid, &hdev->uuids, list) {
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300486 u16 uuid16;
487
488 uuid16 = get_uuid16(uuid->uuid);
489 if (uuid16 == 0)
490 return;
491
492 if (uuid16 < 0x1100)
493 continue;
494
495 if (uuid16 == PNP_INFO_SVCLASS_ID)
496 continue;
497
498 /* Stop if not enough space to put next UUID */
499 if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
500 truncated = 1;
501 break;
502 }
503
504 /* Check for duplicates */
505 for (i = 0; uuid16_list[i] != 0; i++)
506 if (uuid16_list[i] == uuid16)
507 break;
508
509 if (uuid16_list[i] == 0) {
510 uuid16_list[i] = uuid16;
511 eir_len += sizeof(u16);
512 }
513 }
514
515 if (uuid16_list[0] != 0) {
516 u8 *length = ptr;
517
518 /* EIR Data type */
519 ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
520
521 ptr += 2;
522 eir_len += 2;
523
524 for (i = 0; uuid16_list[i] != 0; i++) {
525 *ptr++ = (uuid16_list[i] & 0x00ff);
526 *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
527 }
528
529 /* EIR Data length */
530 *length = (i * sizeof(u16)) + 1;
531 }
532}
533
534static int update_eir(struct hci_dev *hdev)
535{
536 struct hci_cp_write_eir cp;
537
538 if (!(hdev->features[6] & LMP_EXT_INQ))
539 return 0;
540
Johan Hedberg84bde9d2012-01-25 14:21:06 +0200541 if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300542 return 0;
543
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200544 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300545 return 0;
546
547 memset(&cp, 0, sizeof(cp));
548
549 create_eir(hdev, cp.data);
550
551 if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
552 return 0;
553
554 memcpy(hdev->eir, cp.data, sizeof(cp.data));
555
556 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
557}
558
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200559static u8 get_service_classes(struct hci_dev *hdev)
560{
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300561 struct bt_uuid *uuid;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200562 u8 val = 0;
563
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300564 list_for_each_entry(uuid, &hdev->uuids, list)
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200565 val |= uuid->svc_hint;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200566
567 return val;
568}
569
570static int update_class(struct hci_dev *hdev)
571{
572 u8 cod[3];
573
574 BT_DBG("%s", hdev->name);
575
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200576 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200577 return 0;
578
579 cod[0] = hdev->minor_class;
580 cod[1] = hdev->major_class;
581 cod[2] = get_service_classes(hdev);
582
583 if (memcmp(cod, hdev->dev_class, 3) == 0)
584 return 0;
585
586 return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
587}
588
Johan Hedberg7d785252011-12-15 00:47:39 +0200589static void service_cache_off(struct work_struct *work)
590{
591 struct hci_dev *hdev = container_of(work, struct hci_dev,
592 service_cache.work);
593
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200594 if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200595 return;
596
597 hci_dev_lock(hdev);
598
599 update_eir(hdev);
600 update_class(hdev);
601
602 hci_dev_unlock(hdev);
603}
604
605static void mgmt_init_hdev(struct hci_dev *hdev)
606{
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200607 if (!test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200608 INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
609
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200610 if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200611 schedule_delayed_work(&hdev->service_cache,
612 msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
613}
614
Johan Hedberg03811012010-12-08 00:21:06 +0200615static int read_controller_info(struct sock *sk, u16 index)
616{
617 struct mgmt_rp_read_info rp;
618 struct hci_dev *hdev;
619
620 BT_DBG("sock %p hci%u", sk, index);
621
622 hdev = hci_dev_get(index);
623 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200624 return cmd_status(sk, index, MGMT_OP_READ_INFO,
625 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200626
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300627 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200628
Johan Hedberg7d785252011-12-15 00:47:39 +0200629 if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
630 mgmt_init_hdev(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200631
632 memset(&rp, 0, sizeof(rp));
633
Johan Hedberg03811012010-12-08 00:21:06 +0200634 bacpy(&rp.bdaddr, &hdev->bdaddr);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200635
636 rp.version = hdev->hci_ver;
637
Johan Hedberg03811012010-12-08 00:21:06 +0200638 put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200639
640 rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
641 rp.current_settings = cpu_to_le32(get_current_settings(hdev));
642
643 memcpy(rp.dev_class, hdev->dev_class, 3);
Johan Hedberg03811012010-12-08 00:21:06 +0200644
645 memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
646
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300647 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200648 hci_dev_put(hdev);
649
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200650 return cmd_complete(sk, index, MGMT_OP_READ_INFO, 0, &rp, sizeof(rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200651}
652
653static void mgmt_pending_free(struct pending_cmd *cmd)
654{
655 sock_put(cmd->sk);
656 kfree(cmd->param);
657 kfree(cmd);
658}
659
660static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
661 struct hci_dev *hdev,
662 void *data, u16 len)
663{
664 struct pending_cmd *cmd;
665
666 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
667 if (!cmd)
668 return NULL;
669
670 cmd->opcode = opcode;
671 cmd->index = hdev->id;
672
673 cmd->param = kmalloc(len, GFP_ATOMIC);
674 if (!cmd->param) {
675 kfree(cmd);
676 return NULL;
677 }
678
679 if (data)
680 memcpy(cmd->param, data, len);
681
682 cmd->sk = sk;
683 sock_hold(sk);
684
685 list_add(&cmd->list, &hdev->mgmt_pending);
686
687 return cmd;
688}
689
690static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
691 void (*cb)(struct pending_cmd *cmd, void *data),
692 void *data)
693{
694 struct list_head *p, *n;
695
696 list_for_each_safe(p, n, &hdev->mgmt_pending) {
697 struct pending_cmd *cmd;
698
699 cmd = list_entry(p, struct pending_cmd, list);
700
701 if (opcode > 0 && cmd->opcode != opcode)
702 continue;
703
704 cb(cmd, data);
705 }
706}
707
708static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
709{
710 struct pending_cmd *cmd;
711
712 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
713 if (cmd->opcode == opcode)
714 return cmd;
715 }
716
717 return NULL;
718}
719
720static void mgmt_pending_remove(struct pending_cmd *cmd)
721{
722 list_del(&cmd->list);
723 mgmt_pending_free(cmd);
724}
725
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200726static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
Johan Hedberg86805702011-11-11 16:18:52 +0200727{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200728 __le32 settings = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg86805702011-11-11 16:18:52 +0200729
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200730 return cmd_complete(sk, hdev->id, opcode, 0, &settings,
731 sizeof(settings));
Johan Hedberg86805702011-11-11 16:18:52 +0200732}
733
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300734static int set_powered(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200735{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300736 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200737 struct hci_dev *hdev;
738 struct pending_cmd *cmd;
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200739 int err;
Johan Hedberg03811012010-12-08 00:21:06 +0200740
Johan Hedberg03811012010-12-08 00:21:06 +0200741 BT_DBG("request for hci%u", index);
742
743 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200744 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
745 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200746
747 hdev = hci_dev_get(index);
748 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200749 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
750 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200751
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300752 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200753
Marcel Holtmannf0d4b782012-02-21 12:14:25 +0100754 if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
755 cancel_delayed_work(&hdev->power_off);
756
757 if (cp->val) {
758 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
759 mgmt_powered(hdev, 1);
760 goto failed;
761 }
762 }
763
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200764 if (!!cp->val == hdev_is_powered(hdev)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200765 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200766 goto failed;
767 }
768
769 if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200770 err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
771 MGMT_STATUS_BUSY);
Johan Hedberg03811012010-12-08 00:21:06 +0200772 goto failed;
773 }
774
775 cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
776 if (!cmd) {
777 err = -ENOMEM;
778 goto failed;
779 }
780
781 if (cp->val)
Gustavo F. Padovan7f971042011-12-18 12:40:32 -0200782 schedule_work(&hdev->power_on);
Johan Hedberg03811012010-12-08 00:21:06 +0200783 else
Gustavo F. Padovan80b7ab32011-12-17 14:52:27 -0200784 schedule_work(&hdev->power_off.work);
Johan Hedberg03811012010-12-08 00:21:06 +0200785
786 err = 0;
787
788failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300789 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200790 hci_dev_put(hdev);
791 return err;
792}
793
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300794static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200795{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300796 struct mgmt_cp_set_discoverable *cp = data;
Johan Hedberge41d8b42010-12-13 21:07:03 +0200797 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +0200798 struct pending_cmd *cmd;
799 u8 scan;
800 int err;
801
Johan Hedberg03811012010-12-08 00:21:06 +0200802 BT_DBG("request for hci%u", index);
803
804 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200805 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
806 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200807
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200808 hdev = hci_dev_get(index);
809 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200810 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
811 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200812
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300813 hci_dev_lock(hdev);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200814
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200815 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200816 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
817 MGMT_STATUS_NOT_POWERED);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200818 goto failed;
819 }
820
821 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
822 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200823 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
824 MGMT_STATUS_BUSY);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200825 goto failed;
826 }
827
828 if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
829 test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200830 err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200831 goto failed;
832 }
833
834 cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
835 if (!cmd) {
836 err = -ENOMEM;
837 goto failed;
838 }
839
840 scan = SCAN_PAGE;
841
842 if (cp->val)
843 scan |= SCAN_INQUIRY;
844 else
845 cancel_delayed_work(&hdev->discov_off);
846
847 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
848 if (err < 0)
849 mgmt_pending_remove(cmd);
850
Johan Hedberg03811012010-12-08 00:21:06 +0200851 if (cp->val)
852 hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
853
Johan Hedberge41d8b42010-12-13 21:07:03 +0200854failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300855 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200856 hci_dev_put(hdev);
857
858 return err;
859}
860
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300861static int set_connectable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200862{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300863 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200864 struct hci_dev *hdev;
865 struct pending_cmd *cmd;
866 u8 scan;
867 int err;
868
Johan Hedberge41d8b42010-12-13 21:07:03 +0200869 BT_DBG("request for hci%u", index);
870
Johan Hedberg03811012010-12-08 00:21:06 +0200871 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200872 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
873 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200874
875 hdev = hci_dev_get(index);
876 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200877 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
878 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200879
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300880 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200881
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200882 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200883 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
884 MGMT_STATUS_NOT_POWERED);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200885 goto failed;
886 }
887
888 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
889 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200890 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
891 MGMT_STATUS_BUSY);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200892 goto failed;
893 }
894
895 if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200896 err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200897 goto failed;
898 }
899
900 cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
901 if (!cmd) {
902 err = -ENOMEM;
903 goto failed;
904 }
905
906 if (cp->val)
907 scan = SCAN_PAGE;
908 else
909 scan = 0;
910
911 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
912 if (err < 0)
913 mgmt_pending_remove(cmd);
914
915failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300916 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200917 hci_dev_put(hdev);
918
919 return err;
920}
921
922static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
923 u16 data_len, struct sock *skip_sk)
924{
925 struct sk_buff *skb;
926 struct mgmt_hdr *hdr;
927
928 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
929 if (!skb)
930 return -ENOMEM;
931
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200932 hdr = (void *) skb_put(skb, sizeof(*hdr));
933 hdr->opcode = cpu_to_le16(event);
934 if (hdev)
935 hdr->index = cpu_to_le16(hdev->id);
936 else
937 hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
938 hdr->len = cpu_to_le16(data_len);
939
940 if (data)
941 memcpy(skb_put(skb, data_len), data, data_len);
942
Marcel Holtmann470fe1b2012-02-20 14:50:30 +0100943 hci_send_to_control(skb, skip_sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200944 kfree_skb(skb);
945
946 return 0;
947}
948
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300949static int set_pairable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg73f22f62010-12-29 16:00:25 +0200950{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300951 struct mgmt_mode *cp = data;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200952 struct hci_dev *hdev;
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200953 __le32 ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200954 int err;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200955
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200956 BT_DBG("request for hci%u", index);
957
958 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200959 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
960 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200961
962 hdev = hci_dev_get(index);
963 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200964 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
965 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200966
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300967 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200968
969 if (cp->val)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200970 set_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200971 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200972 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200973
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200974 err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200975 if (err < 0)
976 goto failed;
977
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200978 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200979
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200980 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200981
982failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300983 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200984 hci_dev_put(hdev);
985
986 return err;
987}
Johan Hedberg72a734e2010-12-30 00:38:22 +0200988
Johan Hedberg33ef95e2012-02-16 23:56:27 +0200989static int set_link_security(struct sock *sk, u16 index, void *data, u16 len)
990{
991 struct mgmt_mode *cp = data;
992 struct pending_cmd *cmd;
993 struct hci_dev *hdev;
994 uint8_t val;
995 int err;
996
997 BT_DBG("request for hci%u", index);
998
999 if (len != sizeof(*cp))
1000 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1001 MGMT_STATUS_INVALID_PARAMS);
1002
1003 hdev = hci_dev_get(index);
1004 if (!hdev)
1005 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1006 MGMT_STATUS_INVALID_PARAMS);
1007
1008 hci_dev_lock(hdev);
1009
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001010 if (!hdev_is_powered(hdev)) {
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001011 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1012 MGMT_STATUS_NOT_POWERED);
1013 goto failed;
1014 }
1015
1016 if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
1017 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1018 MGMT_STATUS_BUSY);
1019 goto failed;
1020 }
1021
1022 val = !!cp->val;
1023
1024 if (test_bit(HCI_AUTH, &hdev->flags) == val) {
1025 err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
1026 goto failed;
1027 }
1028
1029 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len);
1030 if (!cmd) {
1031 err = -ENOMEM;
1032 goto failed;
1033 }
1034
1035 err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val);
1036 if (err < 0) {
1037 mgmt_pending_remove(cmd);
1038 goto failed;
1039 }
1040
1041failed:
1042 hci_dev_unlock(hdev);
1043 hci_dev_put(hdev);
1044
1045 return err;
1046}
1047
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001048static int set_ssp(struct sock *sk, u16 index, void *data, u16 len)
1049{
1050 struct mgmt_mode *cp = data;
1051 struct pending_cmd *cmd;
1052 struct hci_dev *hdev;
1053 uint8_t val;
1054 int err;
1055
1056 BT_DBG("request for hci%u", index);
1057
1058 if (len != sizeof(*cp))
1059 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1060 MGMT_STATUS_INVALID_PARAMS);
1061
1062 hdev = hci_dev_get(index);
1063 if (!hdev)
1064 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1065 MGMT_STATUS_INVALID_PARAMS);
1066
1067 hci_dev_lock(hdev);
1068
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001069 if (!hdev_is_powered(hdev)) {
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001070 err = cmd_status(sk, index, MGMT_OP_SET_SSP,
1071 MGMT_STATUS_NOT_POWERED);
1072 goto failed;
1073 }
1074
Johan Hedberg1e163572012-02-20 23:53:46 +02001075 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
1076 err = cmd_status(sk, index, MGMT_OP_SET_SSP,
1077 MGMT_STATUS_NOT_SUPPORTED);
1078 goto failed;
1079 }
1080
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001081 if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
1082 err = cmd_status(sk, index, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY);
1083 goto failed;
1084 }
1085
1086 val = !!cp->val;
1087
1088 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
1089 err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
1090 goto failed;
1091 }
1092
1093 cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len);
1094 if (!cmd) {
1095 err = -ENOMEM;
1096 goto failed;
1097 }
1098
1099 err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
1100 if (err < 0) {
1101 mgmt_pending_remove(cmd);
1102 goto failed;
1103 }
1104
1105failed:
1106 hci_dev_unlock(hdev);
1107 hci_dev_put(hdev);
1108
1109 return err;
1110}
1111
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001112static int set_hs(struct sock *sk, u16 index, void *data, u16 len)
1113{
1114 struct mgmt_mode *cp = data;
1115 struct hci_dev *hdev;
1116 int err;
1117
1118 BT_DBG("request for hci%u", index);
1119
1120 if (len != sizeof(*cp))
1121 return cmd_status(sk, index, MGMT_OP_SET_HS,
1122 MGMT_STATUS_INVALID_PARAMS);
1123
1124 hdev = hci_dev_get(index);
1125 if (!hdev)
1126 return cmd_status(sk, index, MGMT_OP_SET_HS,
1127 MGMT_STATUS_INVALID_PARAMS);
1128
1129 if (!enable_hs) {
1130 err = cmd_status(sk, index, MGMT_OP_SET_HS,
1131 MGMT_STATUS_NOT_SUPPORTED);
1132 goto failed;
1133 }
1134
1135 if (cp->val)
1136 set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
1137 else
1138 clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
1139
1140 err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
1141
1142failed:
1143 hci_dev_put(hdev);
1144 return err;
1145}
1146
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001147static int add_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001148{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001149 struct mgmt_cp_add_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001150 struct hci_dev *hdev;
1151 struct bt_uuid *uuid;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001152 int err;
1153
Szymon Janc4e51eae2011-02-25 19:05:48 +01001154 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001155
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001156 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001157 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1158 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001159
Szymon Janc4e51eae2011-02-25 19:05:48 +01001160 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001161 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001162 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1163 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001164
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001165 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001166
1167 uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
1168 if (!uuid) {
1169 err = -ENOMEM;
1170 goto failed;
1171 }
1172
1173 memcpy(uuid->uuid, cp->uuid, 16);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001174 uuid->svc_hint = cp->svc_hint;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001175
1176 list_add(&uuid->list, &hdev->uuids);
1177
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001178 err = update_class(hdev);
1179 if (err < 0)
1180 goto failed;
1181
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001182 err = update_eir(hdev);
1183 if (err < 0)
1184 goto failed;
1185
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001186 err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, 0, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001187
1188failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001189 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001190 hci_dev_put(hdev);
1191
1192 return err;
1193}
1194
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001195static int remove_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001196{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001197 struct mgmt_cp_remove_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001198 struct list_head *p, *n;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001199 struct hci_dev *hdev;
1200 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 +02001201 int err, found;
1202
Szymon Janc4e51eae2011-02-25 19:05:48 +01001203 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001204
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001205 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001206 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1207 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001208
Szymon Janc4e51eae2011-02-25 19:05:48 +01001209 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001210 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001211 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1212 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001213
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001214 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001215
1216 if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
1217 err = hci_uuids_clear(hdev);
1218 goto unlock;
1219 }
1220
1221 found = 0;
1222
1223 list_for_each_safe(p, n, &hdev->uuids) {
1224 struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
1225
1226 if (memcmp(match->uuid, cp->uuid, 16) != 0)
1227 continue;
1228
1229 list_del(&match->list);
1230 found++;
1231 }
1232
1233 if (found == 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001234 err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1235 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001236 goto unlock;
1237 }
1238
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001239 err = update_class(hdev);
1240 if (err < 0)
1241 goto unlock;
1242
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001243 err = update_eir(hdev);
1244 if (err < 0)
1245 goto unlock;
1246
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001247 err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, 0, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001248
1249unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001250 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001251 hci_dev_put(hdev);
1252
1253 return err;
1254}
1255
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001256static int set_dev_class(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001257{
1258 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001259 struct mgmt_cp_set_dev_class *cp = data;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001260 int err;
1261
Szymon Janc4e51eae2011-02-25 19:05:48 +01001262 BT_DBG("request for hci%u", index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001263
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001264 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001265 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1266 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001267
Szymon Janc4e51eae2011-02-25 19:05:48 +01001268 hdev = hci_dev_get(index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001269 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001270 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1271 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001272
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001273 hci_dev_lock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001274
Johan Hedbergb5235a62012-02-21 14:32:24 +02001275 if (!hdev_is_powered(hdev)) {
1276 err = cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1277 MGMT_STATUS_NOT_POWERED);
1278 goto unlock;
1279 }
1280
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001281 hdev->major_class = cp->major;
1282 hdev->minor_class = cp->minor;
1283
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001284 if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
Johan Hedberg7d785252011-12-15 00:47:39 +02001285 hci_dev_unlock(hdev);
1286 cancel_delayed_work_sync(&hdev->service_cache);
1287 hci_dev_lock(hdev);
Johan Hedberg14c0b602011-12-15 00:47:37 +02001288 update_eir(hdev);
Johan Hedberg7d785252011-12-15 00:47:39 +02001289 }
Johan Hedberg14c0b602011-12-15 00:47:37 +02001290
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001291 err = update_class(hdev);
1292
1293 if (err == 0)
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001294 err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, 0,
1295 NULL, 0);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001296
Johan Hedbergb5235a62012-02-21 14:32:24 +02001297unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001298 hci_dev_unlock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001299 hci_dev_put(hdev);
1300
1301 return err;
1302}
1303
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001304static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001305{
1306 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001307 struct mgmt_cp_load_link_keys *cp = data;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001308 u16 key_count, expected_len;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001309 int i;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001310
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001311 if (len < sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001312 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1313 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001314
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001315 key_count = get_unaligned_le16(&cp->key_count);
1316
Johan Hedberg86742e12011-11-07 23:13:38 +02001317 expected_len = sizeof(*cp) + key_count *
1318 sizeof(struct mgmt_link_key_info);
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001319 if (expected_len != len) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001320 BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001321 len, expected_len);
Johan Hedbergca69b792011-11-11 18:10:00 +02001322 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1323 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001324 }
1325
Szymon Janc4e51eae2011-02-25 19:05:48 +01001326 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001327 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001328 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1329 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001330
Szymon Janc4e51eae2011-02-25 19:05:48 +01001331 BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001332 key_count);
1333
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001334 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001335
1336 hci_link_keys_clear(hdev);
1337
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001338 set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001339
1340 if (cp->debug_keys)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001341 set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001342 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001343 clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001344
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001345 for (i = 0; i < key_count; i++) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001346 struct mgmt_link_key_info *key = &cp->keys[i];
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001347
Johan Hedbergd753fdc2012-02-17 14:06:34 +02001348 hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
1349 key->type, key->pin_len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001350 }
1351
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001352 cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
Johan Hedberg0e5f8752011-11-11 16:18:54 +02001353
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001354 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001355 hci_dev_put(hdev);
1356
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001357 return 0;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001358}
1359
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001360static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
1361 u8 addr_type, struct sock *skip_sk)
1362{
1363 struct mgmt_ev_device_unpaired ev;
1364
1365 bacpy(&ev.addr.bdaddr, bdaddr);
1366 ev.addr.type = addr_type;
1367
1368 return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev),
1369 skip_sk);
1370}
1371
Johan Hedberg124f6e32012-02-09 13:50:12 +02001372static int unpair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001373{
1374 struct hci_dev *hdev;
Johan Hedberg124f6e32012-02-09 13:50:12 +02001375 struct mgmt_cp_unpair_device *cp = data;
1376 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001377 struct hci_cp_disconnect dc;
1378 struct pending_cmd *cmd;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001379 struct hci_conn *conn;
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001380 u8 status = 0;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001381 int err;
1382
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001383 if (len != sizeof(*cp))
Johan Hedberg124f6e32012-02-09 13:50:12 +02001384 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001385 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001386
Szymon Janc4e51eae2011-02-25 19:05:48 +01001387 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001388 if (!hdev)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001389 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001390 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001391
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001392 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001393
Johan Hedberga8a1d192011-11-10 15:54:38 +02001394 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02001395 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1396 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001397
Johan Hedberg124f6e32012-02-09 13:50:12 +02001398 if (cp->addr.type == MGMT_ADDR_BREDR)
1399 err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
1400 else
1401 err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
Vinicius Costa Gomesb0dbfb42012-02-02 21:08:03 -03001402
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001403 if (err < 0) {
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001404 status = MGMT_STATUS_NOT_PAIRED;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001405 goto unlock;
1406 }
1407
Johan Hedberga8a1d192011-11-10 15:54:38 +02001408 if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001409 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1410 &rp, sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001411 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001412 goto unlock;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001413 }
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001414
Johan Hedberg124f6e32012-02-09 13:50:12 +02001415 if (cp->addr.type == MGMT_ADDR_BREDR)
1416 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
1417 &cp->addr.bdaddr);
1418 else
1419 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
1420 &cp->addr.bdaddr);
1421
Johan Hedberga8a1d192011-11-10 15:54:38 +02001422 if (!conn) {
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001423 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1424 &rp, sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001425 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberga8a1d192011-11-10 15:54:38 +02001426 goto unlock;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001427 }
1428
Johan Hedberg124f6e32012-02-09 13:50:12 +02001429 cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
1430 sizeof(*cp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02001431 if (!cmd) {
1432 err = -ENOMEM;
1433 goto unlock;
1434 }
1435
1436 put_unaligned_le16(conn->handle, &dc.handle);
1437 dc.reason = 0x13; /* Remote User Terminated Connection */
1438 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1439 if (err < 0)
1440 mgmt_pending_remove(cmd);
1441
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001442unlock:
Johan Hedbergca69b792011-11-11 18:10:00 +02001443 if (err < 0)
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001444 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1445 &rp, sizeof(rp));
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001446 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001447 hci_dev_put(hdev);
1448
1449 return err;
1450}
1451
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001452static int disconnect(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg8962ee72011-01-20 12:40:27 +02001453{
1454 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001455 struct mgmt_cp_disconnect *cp = data;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001456 struct hci_cp_disconnect dc;
Johan Hedberg366a0332011-02-19 12:05:55 -03001457 struct pending_cmd *cmd;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001458 struct hci_conn *conn;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001459 int err;
1460
1461 BT_DBG("");
1462
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001463 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001464 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1465 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001466
Szymon Janc4e51eae2011-02-25 19:05:48 +01001467 hdev = hci_dev_get(index);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001468 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001469 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1470 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001471
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001472 hci_dev_lock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001473
1474 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001475 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1476 MGMT_STATUS_NOT_POWERED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001477 goto failed;
1478 }
1479
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001480 if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001481 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1482 MGMT_STATUS_BUSY);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001483 goto failed;
1484 }
1485
Johan Hedberg88c3df12012-02-09 14:27:38 +02001486 if (cp->addr.type == MGMT_ADDR_BREDR)
1487 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
1488 else
1489 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
Vinicius Costa Gomes365227e2011-05-06 18:41:44 -03001490
Johan Hedberg8962ee72011-01-20 12:40:27 +02001491 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001492 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1493 MGMT_STATUS_NOT_CONNECTED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001494 goto failed;
1495 }
1496
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001497 cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001498 if (!cmd) {
1499 err = -ENOMEM;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001500 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001501 }
Johan Hedberg8962ee72011-01-20 12:40:27 +02001502
1503 put_unaligned_le16(conn->handle, &dc.handle);
1504 dc.reason = 0x13; /* Remote User Terminated Connection */
1505
1506 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1507 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001508 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001509
1510failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001511 hci_dev_unlock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001512 hci_dev_put(hdev);
1513
1514 return err;
1515}
1516
Johan Hedberg48264f02011-11-09 13:58:58 +02001517static u8 link_to_mgmt(u8 link_type, u8 addr_type)
Johan Hedberg4c659c32011-11-07 23:13:39 +02001518{
1519 switch (link_type) {
1520 case LE_LINK:
Johan Hedberg48264f02011-11-09 13:58:58 +02001521 switch (addr_type) {
1522 case ADDR_LE_DEV_PUBLIC:
1523 return MGMT_ADDR_LE_PUBLIC;
1524 case ADDR_LE_DEV_RANDOM:
1525 return MGMT_ADDR_LE_RANDOM;
1526 default:
1527 return MGMT_ADDR_INVALID;
1528 }
Johan Hedberg4c659c32011-11-07 23:13:39 +02001529 case ACL_LINK:
1530 return MGMT_ADDR_BREDR;
1531 default:
1532 return MGMT_ADDR_INVALID;
1533 }
1534}
1535
Szymon Janc8ce62842011-03-01 16:55:32 +01001536static int get_connections(struct sock *sk, u16 index)
Johan Hedberg2784eb42011-01-21 13:56:35 +02001537{
Johan Hedberg2784eb42011-01-21 13:56:35 +02001538 struct mgmt_rp_get_connections *rp;
1539 struct hci_dev *hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001540 struct hci_conn *c;
Johan Hedberga38528f2011-01-22 06:46:43 +02001541 size_t rp_len;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001542 u16 count;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001543 int i, err;
1544
1545 BT_DBG("");
1546
Szymon Janc4e51eae2011-02-25 19:05:48 +01001547 hdev = hci_dev_get(index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001548 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001549 return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
1550 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001551
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001552 hci_dev_lock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001553
1554 count = 0;
Johan Hedbergb644ba32012-01-17 21:48:47 +02001555 list_for_each_entry(c, &hdev->conn_hash.list, list) {
1556 if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1557 count++;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001558 }
1559
Johan Hedberg4c659c32011-11-07 23:13:39 +02001560 rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
Johan Hedberga38528f2011-01-22 06:46:43 +02001561 rp = kmalloc(rp_len, GFP_ATOMIC);
1562 if (!rp) {
Johan Hedberg2784eb42011-01-21 13:56:35 +02001563 err = -ENOMEM;
1564 goto unlock;
1565 }
1566
Johan Hedberg2784eb42011-01-21 13:56:35 +02001567 put_unaligned_le16(count, &rp->conn_count);
1568
Johan Hedberg2784eb42011-01-21 13:56:35 +02001569 i = 0;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001570 list_for_each_entry(c, &hdev->conn_hash.list, list) {
Johan Hedbergb644ba32012-01-17 21:48:47 +02001571 if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1572 continue;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001573 bacpy(&rp->addr[i].bdaddr, &c->dst);
Johan Hedberg48264f02011-11-09 13:58:58 +02001574 rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
Johan Hedberg4c659c32011-11-07 23:13:39 +02001575 if (rp->addr[i].type == MGMT_ADDR_INVALID)
1576 continue;
1577 i++;
1578 }
1579
1580 /* Recalculate length in case of filtered SCO connections, etc */
1581 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberg2784eb42011-01-21 13:56:35 +02001582
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001583 err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, 0, rp, rp_len);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001584
1585unlock:
Johan Hedberga38528f2011-01-22 06:46:43 +02001586 kfree(rp);
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001587 hci_dev_unlock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001588 hci_dev_put(hdev);
1589 return err;
1590}
1591
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001592static int send_pin_code_neg_reply(struct sock *sk, u16 index,
1593 struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
1594{
1595 struct pending_cmd *cmd;
1596 int err;
1597
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001598 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001599 sizeof(*cp));
1600 if (!cmd)
1601 return -ENOMEM;
1602
Johan Hedbergd8457692012-02-17 14:24:57 +02001603 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
1604 sizeof(cp->addr.bdaddr), &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001605 if (err < 0)
1606 mgmt_pending_remove(cmd);
1607
1608 return err;
1609}
1610
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001611static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001612{
1613 struct hci_dev *hdev;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001614 struct hci_conn *conn;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001615 struct mgmt_cp_pin_code_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001616 struct hci_cp_pin_code_reply reply;
Johan Hedberg366a0332011-02-19 12:05:55 -03001617 struct pending_cmd *cmd;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001618 int err;
1619
1620 BT_DBG("");
1621
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001622 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001623 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1624 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001625
Szymon Janc4e51eae2011-02-25 19:05:48 +01001626 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001627 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001628 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1629 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001630
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001631 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001632
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001633 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001634 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1635 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001636 goto failed;
1637 }
1638
Johan Hedbergd8457692012-02-17 14:24:57 +02001639 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001640 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001641 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1642 MGMT_STATUS_NOT_CONNECTED);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001643 goto failed;
1644 }
1645
1646 if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
Johan Hedbergd8457692012-02-17 14:24:57 +02001647 struct mgmt_cp_pin_code_neg_reply ncp;
1648
1649 memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr));
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001650
1651 BT_ERR("PIN code is not 16 bytes long");
1652
1653 err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
1654 if (err >= 0)
1655 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001656 MGMT_STATUS_INVALID_PARAMS);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001657
1658 goto failed;
1659 }
1660
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001661 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data,
1662 len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001663 if (!cmd) {
1664 err = -ENOMEM;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001665 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001666 }
Johan Hedberg980e1a52011-01-22 06:10:07 +02001667
Johan Hedbergd8457692012-02-17 14:24:57 +02001668 bacpy(&reply.bdaddr, &cp->addr.bdaddr);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001669 reply.pin_len = cp->pin_len;
Waldemar Rymarkiewicz24718ca2011-06-01 17:28:47 +02001670 memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
Johan Hedberg980e1a52011-01-22 06:10:07 +02001671
1672 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
1673 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001674 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001675
1676failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001677 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001678 hci_dev_put(hdev);
1679
1680 return err;
1681}
1682
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001683static int pin_code_neg_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001684{
1685 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001686 struct mgmt_cp_pin_code_neg_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001687 int err;
1688
1689 BT_DBG("");
1690
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001691 if (len != sizeof(*cp))
1692 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001693 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001694
Szymon Janc4e51eae2011-02-25 19:05:48 +01001695 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001696 if (!hdev)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001697 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001698 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001699
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001700 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001701
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001702 if (!hdev_is_powered(hdev)) {
Szymon Janc4e51eae2011-02-25 19:05:48 +01001703 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001704 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001705 goto failed;
1706 }
1707
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001708 err = send_pin_code_neg_reply(sk, index, hdev, cp);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001709
1710failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001711 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001712 hci_dev_put(hdev);
1713
1714 return err;
1715}
1716
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001717static int set_io_capability(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001718{
1719 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001720 struct mgmt_cp_set_io_capability *cp = data;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001721
1722 BT_DBG("");
1723
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001724 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001725 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1726 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001727
Szymon Janc4e51eae2011-02-25 19:05:48 +01001728 hdev = hci_dev_get(index);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001729 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001730 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1731 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001732
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001733 hci_dev_lock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001734
1735 hdev->io_capability = cp->io_capability;
1736
1737 BT_DBG("%s IO capability set to 0x%02x", hdev->name,
Szymon Jancb8534e02011-03-01 16:55:34 +01001738 hdev->io_capability);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001739
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001740 hci_dev_unlock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001741 hci_dev_put(hdev);
1742
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001743 return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, 0);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001744}
1745
Johan Hedberge9a416b2011-02-19 12:05:56 -03001746static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1747{
1748 struct hci_dev *hdev = conn->hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001749 struct pending_cmd *cmd;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001750
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001751 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
Johan Hedberge9a416b2011-02-19 12:05:56 -03001752 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1753 continue;
1754
Johan Hedberge9a416b2011-02-19 12:05:56 -03001755 if (cmd->user_data != conn)
1756 continue;
1757
1758 return cmd;
1759 }
1760
1761 return NULL;
1762}
1763
1764static void pairing_complete(struct pending_cmd *cmd, u8 status)
1765{
1766 struct mgmt_rp_pair_device rp;
1767 struct hci_conn *conn = cmd->user_data;
1768
Johan Hedbergba4e5642011-11-11 00:07:34 +02001769 bacpy(&rp.addr.bdaddr, &conn->dst);
1770 rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001771
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001772 cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
1773 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001774
1775 /* So we don't get further callbacks for this connection */
1776 conn->connect_cfm_cb = NULL;
1777 conn->security_cfm_cb = NULL;
1778 conn->disconn_cfm_cb = NULL;
1779
1780 hci_conn_put(conn);
1781
Johan Hedberga664b5b2011-02-19 12:06:02 -03001782 mgmt_pending_remove(cmd);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001783}
1784
1785static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1786{
1787 struct pending_cmd *cmd;
1788
1789 BT_DBG("status %u", status);
1790
Johan Hedberg56e5cb82011-11-08 20:40:16 +02001791 cmd = find_pairing(conn);
1792 if (!cmd)
1793 BT_DBG("Unable to find a pending command");
1794 else
Johan Hedberge2113262012-02-18 15:20:03 +02001795 pairing_complete(cmd, mgmt_status(status));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001796}
1797
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001798static int pair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001799{
1800 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001801 struct mgmt_cp_pair_device *cp = data;
Johan Hedberg1425acb2011-11-11 00:07:35 +02001802 struct mgmt_rp_pair_device rp;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001803 struct pending_cmd *cmd;
1804 u8 sec_level, auth_type;
1805 struct hci_conn *conn;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001806 int err;
1807
1808 BT_DBG("");
1809
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001810 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001811 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1812 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001813
Szymon Janc4e51eae2011-02-25 19:05:48 +01001814 hdev = hci_dev_get(index);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001815 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001816 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1817 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001818
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001819 hci_dev_lock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001820
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001821 sec_level = BT_SECURITY_MEDIUM;
1822 if (cp->io_cap == 0x03)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001823 auth_type = HCI_AT_DEDICATED_BONDING;
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001824 else
Johan Hedberge9a416b2011-02-19 12:05:56 -03001825 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001826
Johan Hedbergba4e5642011-11-11 00:07:34 +02001827 if (cp->addr.type == MGMT_ADDR_BREDR)
1828 conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001829 auth_type);
1830 else
Johan Hedbergba4e5642011-11-11 00:07:34 +02001831 conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001832 auth_type);
1833
Johan Hedberg1425acb2011-11-11 00:07:35 +02001834 memset(&rp, 0, sizeof(rp));
1835 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1836 rp.addr.type = cp->addr.type;
1837
Ville Tervo30e76272011-02-22 16:10:53 -03001838 if (IS_ERR(conn)) {
Johan Hedberge2113262012-02-18 15:20:03 +02001839 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1840 MGMT_STATUS_CONNECT_FAILED,
1841 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001842 goto unlock;
1843 }
1844
1845 if (conn->connect_cfm_cb) {
1846 hci_conn_put(conn);
Johan Hedberge2113262012-02-18 15:20:03 +02001847 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1848 MGMT_STATUS_BUSY, &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001849 goto unlock;
1850 }
1851
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001852 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001853 if (!cmd) {
1854 err = -ENOMEM;
1855 hci_conn_put(conn);
1856 goto unlock;
1857 }
1858
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001859 /* For LE, just connecting isn't a proof that the pairing finished */
Johan Hedbergba4e5642011-11-11 00:07:34 +02001860 if (cp->addr.type == MGMT_ADDR_BREDR)
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001861 conn->connect_cfm_cb = pairing_complete_cb;
1862
Johan Hedberge9a416b2011-02-19 12:05:56 -03001863 conn->security_cfm_cb = pairing_complete_cb;
1864 conn->disconn_cfm_cb = pairing_complete_cb;
1865 conn->io_capability = cp->io_cap;
1866 cmd->user_data = conn;
1867
1868 if (conn->state == BT_CONNECTED &&
1869 hci_conn_security(conn, sec_level, auth_type))
1870 pairing_complete(cmd, 0);
1871
1872 err = 0;
1873
1874unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001875 hci_dev_unlock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001876 hci_dev_put(hdev);
1877
1878 return err;
1879}
1880
Johan Hedberg28424702012-02-02 04:02:29 +02001881static int cancel_pair_device(struct sock *sk, u16 index,
1882 unsigned char *data, u16 len)
1883{
1884 struct mgmt_addr_info *addr = (void *) data;
1885 struct hci_dev *hdev;
1886 struct pending_cmd *cmd;
1887 struct hci_conn *conn;
1888 int err;
1889
1890 BT_DBG("");
1891
1892 if (len != sizeof(*addr))
1893 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1894 MGMT_STATUS_INVALID_PARAMS);
1895
1896 hdev = hci_dev_get(index);
1897 if (!hdev)
1898 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1899 MGMT_STATUS_INVALID_PARAMS);
1900
1901 hci_dev_lock(hdev);
1902
1903 cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
1904 if (!cmd) {
1905 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1906 MGMT_STATUS_INVALID_PARAMS);
1907 goto unlock;
1908 }
1909
1910 conn = cmd->user_data;
1911
1912 if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
1913 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1914 MGMT_STATUS_INVALID_PARAMS);
1915 goto unlock;
1916 }
1917
1918 pairing_complete(cmd, MGMT_STATUS_CANCELLED);
1919
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001920 err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr,
Johan Hedberg28424702012-02-02 04:02:29 +02001921 sizeof(*addr));
1922unlock:
1923 hci_dev_unlock(hdev);
1924 hci_dev_put(hdev);
1925
1926 return err;
1927}
1928
Brian Gix0df4c182011-11-16 13:53:13 -08001929static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02001930 u8 type, u16 mgmt_op, u16 hci_op,
1931 __le32 passkey)
Johan Hedberga5c29682011-02-19 12:05:57 -03001932{
Johan Hedberga5c29682011-02-19 12:05:57 -03001933 struct pending_cmd *cmd;
1934 struct hci_dev *hdev;
Brian Gix0df4c182011-11-16 13:53:13 -08001935 struct hci_conn *conn;
Johan Hedberga5c29682011-02-19 12:05:57 -03001936 int err;
1937
Szymon Janc4e51eae2011-02-25 19:05:48 +01001938 hdev = hci_dev_get(index);
Johan Hedberga5c29682011-02-19 12:05:57 -03001939 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001940 return cmd_status(sk, index, mgmt_op,
1941 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberga5c29682011-02-19 12:05:57 -03001942
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001943 hci_dev_lock(hdev);
Johan Hedberg08ba5382011-03-16 14:29:34 +02001944
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001945 if (!hdev_is_powered(hdev)) {
Brian Gix0df4c182011-11-16 13:53:13 -08001946 err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
1947 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001948 }
1949
Johan Hedberg272d90d2012-02-09 15:26:12 +02001950 if (type == MGMT_ADDR_BREDR)
1951 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
1952 else
Brian Gix47c15e22011-11-16 13:53:14 -08001953 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
Brian Gix47c15e22011-11-16 13:53:14 -08001954
Johan Hedberg272d90d2012-02-09 15:26:12 +02001955 if (!conn) {
1956 err = cmd_status(sk, index, mgmt_op,
1957 MGMT_STATUS_NOT_CONNECTED);
1958 goto done;
1959 }
1960
1961 if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
Brian Gix47c15e22011-11-16 13:53:14 -08001962 /* Continue with pairing via SMP */
Brian Gix5fe57d92011-12-21 16:12:13 -08001963 err = smp_user_confirm_reply(conn, mgmt_op, passkey);
Brian Gix47c15e22011-11-16 13:53:14 -08001964
Brian Gix5fe57d92011-12-21 16:12:13 -08001965 if (!err)
1966 err = cmd_status(sk, index, mgmt_op,
1967 MGMT_STATUS_SUCCESS);
1968 else
1969 err = cmd_status(sk, index, mgmt_op,
1970 MGMT_STATUS_FAILED);
1971
Brian Gix47c15e22011-11-16 13:53:14 -08001972 goto done;
1973 }
1974
Brian Gix0df4c182011-11-16 13:53:13 -08001975 cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
Johan Hedberga5c29682011-02-19 12:05:57 -03001976 if (!cmd) {
1977 err = -ENOMEM;
Brian Gix0df4c182011-11-16 13:53:13 -08001978 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001979 }
1980
Brian Gix0df4c182011-11-16 13:53:13 -08001981 /* Continue with pairing via HCI */
Brian Gix604086b2011-11-23 08:28:33 -08001982 if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
1983 struct hci_cp_user_passkey_reply cp;
1984
1985 bacpy(&cp.bdaddr, bdaddr);
1986 cp.passkey = passkey;
1987 err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
1988 } else
1989 err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
1990
Johan Hedberga664b5b2011-02-19 12:06:02 -03001991 if (err < 0)
1992 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03001993
Brian Gix0df4c182011-11-16 13:53:13 -08001994done:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001995 hci_dev_unlock(hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03001996 hci_dev_put(hdev);
1997
1998 return err;
1999}
2000
Brian Gix0df4c182011-11-16 13:53:13 -08002001static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
2002{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002003 struct mgmt_cp_user_confirm_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08002004
2005 BT_DBG("");
2006
2007 if (len != sizeof(*cp))
2008 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
2009 MGMT_STATUS_INVALID_PARAMS);
2010
Johan Hedberg272d90d2012-02-09 15:26:12 +02002011 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2012 MGMT_OP_USER_CONFIRM_REPLY,
2013 HCI_OP_USER_CONFIRM_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08002014}
2015
2016static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
2017 u16 len)
2018{
Johan Hedbergc9c26592011-12-15 00:47:41 +02002019 struct mgmt_cp_user_confirm_neg_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08002020
2021 BT_DBG("");
2022
2023 if (len != sizeof(*cp))
2024 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
2025 MGMT_STATUS_INVALID_PARAMS);
2026
Johan Hedberg272d90d2012-02-09 15:26:12 +02002027 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2028 MGMT_OP_USER_CONFIRM_NEG_REPLY,
2029 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08002030}
2031
Brian Gix604086b2011-11-23 08:28:33 -08002032static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
2033{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002034 struct mgmt_cp_user_passkey_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08002035
2036 BT_DBG("");
2037
2038 if (len != sizeof(*cp))
2039 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
2040 EINVAL);
2041
Johan Hedberg272d90d2012-02-09 15:26:12 +02002042 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2043 MGMT_OP_USER_PASSKEY_REPLY,
2044 HCI_OP_USER_PASSKEY_REPLY,
2045 cp->passkey);
Brian Gix604086b2011-11-23 08:28:33 -08002046}
2047
2048static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
2049 u16 len)
2050{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002051 struct mgmt_cp_user_passkey_neg_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08002052
2053 BT_DBG("");
2054
2055 if (len != sizeof(*cp))
2056 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
2057 EINVAL);
2058
Johan Hedberg272d90d2012-02-09 15:26:12 +02002059 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2060 MGMT_OP_USER_PASSKEY_NEG_REPLY,
2061 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
Brian Gix604086b2011-11-23 08:28:33 -08002062}
2063
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002064static int set_local_name(struct sock *sk, u16 index, void *data,
Johan Hedbergb312b1612011-03-16 14:29:37 +02002065 u16 len)
2066{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002067 struct mgmt_cp_set_local_name *mgmt_cp = data;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002068 struct hci_cp_write_local_name hci_cp;
2069 struct hci_dev *hdev;
2070 struct pending_cmd *cmd;
2071 int err;
2072
2073 BT_DBG("");
2074
2075 if (len != sizeof(*mgmt_cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02002076 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2077 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002078
2079 hdev = hci_dev_get(index);
2080 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002081 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2082 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002083
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002084 hci_dev_lock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002085
Johan Hedbergb5235a62012-02-21 14:32:24 +02002086 if (!hdev_is_powered(hdev)) {
2087 err = cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2088 MGMT_STATUS_NOT_POWERED);
2089 goto failed;
2090 }
2091
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002092 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data,
2093 len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002094 if (!cmd) {
2095 err = -ENOMEM;
2096 goto failed;
2097 }
2098
2099 memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
2100 err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
2101 &hci_cp);
2102 if (err < 0)
2103 mgmt_pending_remove(cmd);
2104
2105failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002106 hci_dev_unlock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002107 hci_dev_put(hdev);
2108
2109 return err;
2110}
2111
Szymon Jancc35938b2011-03-22 13:12:21 +01002112static int read_local_oob_data(struct sock *sk, u16 index)
2113{
2114 struct hci_dev *hdev;
2115 struct pending_cmd *cmd;
2116 int err;
2117
2118 BT_DBG("hci%u", index);
2119
2120 hdev = hci_dev_get(index);
2121 if (!hdev)
2122 return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002123 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancc35938b2011-03-22 13:12:21 +01002124
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002125 hci_dev_lock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002126
Johan Hedberg4b34ee782012-02-21 14:13:02 +02002127 if (!hdev_is_powered(hdev)) {
Szymon Jancc35938b2011-03-22 13:12:21 +01002128 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002129 MGMT_STATUS_NOT_POWERED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002130 goto unlock;
2131 }
2132
2133 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
2134 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002135 MGMT_STATUS_NOT_SUPPORTED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002136 goto unlock;
2137 }
2138
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002139 if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002140 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
2141 MGMT_STATUS_BUSY);
Szymon Jancc35938b2011-03-22 13:12:21 +01002142 goto unlock;
2143 }
2144
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002145 cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
Szymon Jancc35938b2011-03-22 13:12:21 +01002146 if (!cmd) {
2147 err = -ENOMEM;
2148 goto unlock;
2149 }
2150
2151 err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
2152 if (err < 0)
2153 mgmt_pending_remove(cmd);
2154
2155unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002156 hci_dev_unlock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002157 hci_dev_put(hdev);
2158
2159 return err;
2160}
2161
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002162static int add_remote_oob_data(struct sock *sk, u16 index, void *data,
2163 u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002164{
2165 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002166 struct mgmt_cp_add_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002167 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002168 int err;
2169
2170 BT_DBG("hci%u ", index);
2171
2172 if (len != sizeof(*cp))
2173 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002174 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002175
2176 hdev = hci_dev_get(index);
2177 if (!hdev)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002178 return cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
2179 MGMT_STATUS_INVALID_PARAMS,
2180 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002181
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002182 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002183
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002184 err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
Szymon Janc2763eda2011-03-22 13:12:22 +01002185 cp->randomizer);
2186 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002187 status = MGMT_STATUS_FAILED;
Szymon Janc2763eda2011-03-22 13:12:22 +01002188 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002189 status = 0;
2190
2191 err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
2192 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002193
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002194 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002195 hci_dev_put(hdev);
2196
2197 return err;
2198}
2199
2200static int remove_remote_oob_data(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002201 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002202{
2203 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002204 struct mgmt_cp_remove_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002205 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002206 int err;
2207
2208 BT_DBG("hci%u ", index);
2209
2210 if (len != sizeof(*cp))
2211 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002212 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002213
2214 hdev = hci_dev_get(index);
2215 if (!hdev)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002216 return cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
2217 MGMT_STATUS_INVALID_PARAMS,
2218 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002219
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002220 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002221
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002222 err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
Szymon Janc2763eda2011-03-22 13:12:22 +01002223 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002224 status = MGMT_STATUS_INVALID_PARAMS;
Szymon Janc2763eda2011-03-22 13:12:22 +01002225 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002226 status = 0;
2227
2228 err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status,
2229 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002230
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002231 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002232 hci_dev_put(hdev);
2233
2234 return err;
2235}
2236
Andre Guedes5e0452c2012-02-17 20:39:38 -03002237static int discovery(struct hci_dev *hdev)
2238{
2239 int err;
2240
2241 if (lmp_host_le_capable(hdev)) {
2242 if (lmp_bredr_capable(hdev)) {
2243 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2244 LE_SCAN_INT, LE_SCAN_WIN,
2245 LE_SCAN_TIMEOUT_BREDR_LE);
2246 } else {
2247 hdev->discovery.type = DISCOV_TYPE_LE;
2248 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2249 LE_SCAN_INT, LE_SCAN_WIN,
2250 LE_SCAN_TIMEOUT_LE_ONLY);
2251 }
2252 } else {
2253 hdev->discovery.type = DISCOV_TYPE_BREDR;
2254 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
2255 }
2256
2257 return err;
2258}
2259
2260int mgmt_interleaved_discovery(struct hci_dev *hdev)
2261{
2262 int err;
2263
2264 BT_DBG("%s", hdev->name);
2265
2266 hci_dev_lock(hdev);
2267
2268 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
2269 if (err < 0)
2270 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2271
2272 hci_dev_unlock(hdev);
2273
2274 return err;
2275}
2276
Johan Hedberg450dfda2011-11-12 11:58:22 +02002277static int start_discovery(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002278 void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002279{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002280 struct mgmt_cp_start_discovery *cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002281 struct pending_cmd *cmd;
2282 struct hci_dev *hdev;
2283 int err;
2284
2285 BT_DBG("hci%u", index);
2286
Johan Hedberg450dfda2011-11-12 11:58:22 +02002287 if (len != sizeof(*cp))
2288 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2289 MGMT_STATUS_INVALID_PARAMS);
2290
Johan Hedberg14a53662011-04-27 10:29:56 -04002291 hdev = hci_dev_get(index);
2292 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002293 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2294 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002295
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002296 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002297
Johan Hedberg4b34ee782012-02-21 14:13:02 +02002298 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002299 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2300 MGMT_STATUS_NOT_POWERED);
Johan Hedbergbd2d1332011-11-07 23:13:37 +02002301 goto failed;
2302 }
2303
Johan Hedbergff9ef572012-01-04 14:23:45 +02002304 if (hdev->discovery.state != DISCOVERY_STOPPED) {
2305 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2306 MGMT_STATUS_BUSY);
2307 goto failed;
2308 }
2309
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002310 cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002311 if (!cmd) {
2312 err = -ENOMEM;
2313 goto failed;
2314 }
2315
Andre Guedes4aab14e2012-02-17 20:39:36 -03002316 hdev->discovery.type = cp->type;
2317
2318 switch (hdev->discovery.type) {
Andre Guedesf39799f2012-02-17 20:39:35 -03002319 case DISCOV_TYPE_BREDR:
Andre Guedes3fd24152012-02-03 17:48:01 -03002320 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
Andre Guedesf39799f2012-02-17 20:39:35 -03002321 break;
2322
2323 case DISCOV_TYPE_LE:
Andre Guedes3fd24152012-02-03 17:48:01 -03002324 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
2325 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
Andre Guedesf39799f2012-02-17 20:39:35 -03002326 break;
2327
Andre Guedes5e0452c2012-02-17 20:39:38 -03002328 case DISCOV_TYPE_INTERLEAVED:
2329 err = discovery(hdev);
2330 break;
2331
Andre Guedesf39799f2012-02-17 20:39:35 -03002332 default:
Andre Guedes3fd24152012-02-03 17:48:01 -03002333 err = -EINVAL;
Andre Guedesf39799f2012-02-17 20:39:35 -03002334 }
Andre Guedes3fd24152012-02-03 17:48:01 -03002335
Johan Hedberg14a53662011-04-27 10:29:56 -04002336 if (err < 0)
2337 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002338 else
2339 hci_discovery_set_state(hdev, DISCOVERY_STARTING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002340
2341failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002342 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002343 hci_dev_put(hdev);
2344
2345 return err;
2346}
2347
Johan Hedbergd9306502012-02-20 23:25:18 +02002348static int stop_discovery(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002349{
Johan Hedbergd9306502012-02-20 23:25:18 +02002350 struct mgmt_cp_stop_discovery *mgmt_cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002351 struct hci_dev *hdev;
2352 struct pending_cmd *cmd;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002353 struct hci_cp_remote_name_req_cancel cp;
2354 struct inquiry_entry *e;
Johan Hedberg14a53662011-04-27 10:29:56 -04002355 int err;
2356
2357 BT_DBG("hci%u", index);
2358
Johan Hedbergd9306502012-02-20 23:25:18 +02002359 if (len != sizeof(*mgmt_cp))
2360 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2361 MGMT_STATUS_INVALID_PARAMS);
2362
Johan Hedberg14a53662011-04-27 10:29:56 -04002363 hdev = hci_dev_get(index);
2364 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002365 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2366 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002367
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002368 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002369
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002370 if (!hci_discovery_active(hdev)) {
Johan Hedbergd9306502012-02-20 23:25:18 +02002371 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY,
2372 MGMT_STATUS_REJECTED,
2373 &mgmt_cp->type, sizeof(mgmt_cp->type));
2374 goto unlock;
2375 }
2376
2377 if (hdev->discovery.type != mgmt_cp->type) {
2378 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY,
2379 MGMT_STATUS_INVALID_PARAMS,
2380 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002381 goto unlock;
Johan Hedbergff9ef572012-01-04 14:23:45 +02002382 }
2383
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002384 cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002385 if (!cmd) {
2386 err = -ENOMEM;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002387 goto unlock;
Johan Hedberg14a53662011-04-27 10:29:56 -04002388 }
2389
Andre Guedes343f9352012-02-17 20:39:37 -03002390 if (hdev->discovery.state == DISCOVERY_FINDING) {
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002391 err = hci_cancel_inquiry(hdev);
2392 if (err < 0)
2393 mgmt_pending_remove(cmd);
2394 else
2395 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
2396 goto unlock;
2397 }
2398
2399 e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
2400 if (!e) {
2401 mgmt_pending_remove(cmd);
Johan Hedbergaee9b2182012-02-18 15:07:59 +02002402 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, 0,
Johan Hedbergd9306502012-02-20 23:25:18 +02002403 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002404 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2405 goto unlock;
2406 }
2407
2408 bacpy(&cp.bdaddr, &e->data.bdaddr);
2409 err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
2410 sizeof(cp), &cp);
Johan Hedberg14a53662011-04-27 10:29:56 -04002411 if (err < 0)
2412 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002413 else
2414 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002415
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002416unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002417 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002418 hci_dev_put(hdev);
2419
2420 return err;
2421}
2422
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002423static int confirm_name(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg561aafb2012-01-04 13:31:59 +02002424{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002425 struct mgmt_cp_confirm_name *cp = data;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002426 struct inquiry_entry *e;
2427 struct hci_dev *hdev;
2428 int err;
2429
2430 BT_DBG("hci%u", index);
2431
2432 if (len != sizeof(*cp))
2433 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2434 MGMT_STATUS_INVALID_PARAMS);
2435
2436 hdev = hci_dev_get(index);
2437 if (!hdev)
2438 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2439 MGMT_STATUS_INVALID_PARAMS);
2440
2441 hci_dev_lock(hdev);
2442
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002443 if (!hci_discovery_active(hdev)) {
2444 err = cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2445 MGMT_STATUS_FAILED);
2446 goto failed;
2447 }
2448
Johan Hedberga198e7b2012-02-17 14:27:06 +02002449 e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002450 if (!e) {
2451 err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME,
2452 MGMT_STATUS_INVALID_PARAMS);
2453 goto failed;
2454 }
2455
2456 if (cp->name_known) {
2457 e->name_state = NAME_KNOWN;
2458 list_del(&e->list);
2459 } else {
2460 e->name_state = NAME_NEEDED;
Johan Hedberga3d4e202012-01-09 00:53:02 +02002461 hci_inquiry_cache_update_resolve(hdev, e);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002462 }
2463
2464 err = 0;
2465
2466failed:
2467 hci_dev_unlock(hdev);
2468
2469 return err;
2470}
2471
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002472static int block_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002473{
2474 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002475 struct mgmt_cp_block_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002476 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002477 int err;
2478
2479 BT_DBG("hci%u", index);
2480
Antti Julku7fbec222011-06-15 12:01:15 +03002481 if (len != sizeof(*cp))
2482 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002483 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002484
2485 hdev = hci_dev_get(index);
2486 if (!hdev)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002487 return cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
2488 MGMT_STATUS_INVALID_PARAMS,
2489 &cp->addr, sizeof(cp->addr));
Antti Julku7fbec222011-06-15 12:01:15 +03002490
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002491 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002492
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002493 err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002494 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002495 status = MGMT_STATUS_FAILED;
Antti Julku7fbec222011-06-15 12:01:15 +03002496 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002497 status = 0;
2498
2499 err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, status,
2500 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002501
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002502 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002503 hci_dev_put(hdev);
2504
2505 return err;
2506}
2507
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002508static int unblock_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002509{
2510 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002511 struct mgmt_cp_unblock_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002512 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002513 int err;
2514
2515 BT_DBG("hci%u", index);
2516
Antti Julku7fbec222011-06-15 12:01:15 +03002517 if (len != sizeof(*cp))
2518 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002519 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002520
2521 hdev = hci_dev_get(index);
2522 if (!hdev)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002523 return cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2524 MGMT_STATUS_INVALID_PARAMS,
2525 &cp->addr, sizeof(cp->addr));
Antti Julku7fbec222011-06-15 12:01:15 +03002526
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002527 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002528
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002529 err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002530 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002531 status = MGMT_STATUS_INVALID_PARAMS;
Antti Julku7fbec222011-06-15 12:01:15 +03002532 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002533 status = 0;
2534
2535 err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, status,
2536 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002537
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002538 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002539 hci_dev_put(hdev);
2540
2541 return err;
2542}
2543
Antti Julkuf6422ec2011-06-22 13:11:56 +03002544static int set_fast_connectable(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002545 void *data, u16 len)
Antti Julkuf6422ec2011-06-22 13:11:56 +03002546{
2547 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002548 struct mgmt_mode *cp = data;
Antti Julkuf6422ec2011-06-22 13:11:56 +03002549 struct hci_cp_write_page_scan_activity acp;
2550 u8 type;
2551 int err;
2552
2553 BT_DBG("hci%u", index);
2554
2555 if (len != sizeof(*cp))
2556 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002557 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002558
2559 hdev = hci_dev_get(index);
2560 if (!hdev)
2561 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002562 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002563
2564 hci_dev_lock(hdev);
2565
Johan Hedbergf7c68692011-12-15 00:47:36 +02002566 if (cp->val) {
Antti Julkuf6422ec2011-06-22 13:11:56 +03002567 type = PAGE_SCAN_TYPE_INTERLACED;
2568 acp.interval = 0x0024; /* 22.5 msec page scan interval */
2569 } else {
2570 type = PAGE_SCAN_TYPE_STANDARD; /* default */
2571 acp.interval = 0x0800; /* default 1.28 sec page scan */
2572 }
2573
2574 acp.window = 0x0012; /* default 11.25 msec page scan window */
2575
2576 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
2577 sizeof(acp), &acp);
2578 if (err < 0) {
2579 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002580 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002581 goto done;
2582 }
2583
2584 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
2585 if (err < 0) {
2586 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002587 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002588 goto done;
2589 }
2590
Johan Hedbergaee9b2182012-02-18 15:07:59 +02002591 err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, 0,
2592 NULL, 0);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002593done:
2594 hci_dev_unlock(hdev);
2595 hci_dev_put(hdev);
2596
2597 return err;
2598}
2599
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002600static int load_long_term_keys(struct sock *sk, u16 index,
2601 void *cp_data, u16 len)
2602{
2603 struct hci_dev *hdev;
2604 struct mgmt_cp_load_long_term_keys *cp = cp_data;
2605 u16 key_count, expected_len;
2606 int i;
2607
2608 if (len < sizeof(*cp))
2609 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2610 EINVAL);
2611
2612 key_count = get_unaligned_le16(&cp->key_count);
2613
2614 expected_len = sizeof(*cp) + key_count *
2615 sizeof(struct mgmt_ltk_info);
2616 if (expected_len != len) {
2617 BT_ERR("load_keys: expected %u bytes, got %u bytes",
2618 len, expected_len);
2619 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2620 EINVAL);
2621 }
2622
2623 hdev = hci_dev_get(index);
2624 if (!hdev)
2625 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2626 ENODEV);
2627
2628 BT_DBG("hci%u key_count %u", index, key_count);
2629
2630 hci_dev_lock(hdev);
2631
2632 hci_smp_ltks_clear(hdev);
2633
2634 for (i = 0; i < key_count; i++) {
2635 struct mgmt_ltk_info *key = &cp->keys[i];
2636 u8 type;
2637
2638 if (key->master)
2639 type = HCI_SMP_LTK;
2640 else
2641 type = HCI_SMP_LTK_SLAVE;
2642
2643 hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
2644 type, 0, key->authenticated, key->val,
2645 key->enc_size, key->ediv, key->rand);
2646 }
2647
2648 hci_dev_unlock(hdev);
2649 hci_dev_put(hdev);
2650
2651 return 0;
2652}
2653
Johan Hedberg03811012010-12-08 00:21:06 +02002654int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
2655{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002656 void *buf;
2657 u8 *cp;
Johan Hedberg03811012010-12-08 00:21:06 +02002658 struct mgmt_hdr *hdr;
Szymon Janc4e51eae2011-02-25 19:05:48 +01002659 u16 opcode, index, len;
Johan Hedberg03811012010-12-08 00:21:06 +02002660 int err;
2661
2662 BT_DBG("got %zu bytes", msglen);
2663
2664 if (msglen < sizeof(*hdr))
2665 return -EINVAL;
2666
Gustavo F. Padovane63a15e2011-04-04 18:56:53 -03002667 buf = kmalloc(msglen, GFP_KERNEL);
Johan Hedberg03811012010-12-08 00:21:06 +02002668 if (!buf)
2669 return -ENOMEM;
2670
2671 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
2672 err = -EFAULT;
2673 goto done;
2674 }
2675
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002676 hdr = buf;
Johan Hedberg03811012010-12-08 00:21:06 +02002677 opcode = get_unaligned_le16(&hdr->opcode);
Szymon Janc4e51eae2011-02-25 19:05:48 +01002678 index = get_unaligned_le16(&hdr->index);
Johan Hedberg03811012010-12-08 00:21:06 +02002679 len = get_unaligned_le16(&hdr->len);
2680
2681 if (len != msglen - sizeof(*hdr)) {
2682 err = -EINVAL;
2683 goto done;
2684 }
2685
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002686 cp = buf + sizeof(*hdr);
2687
Johan Hedberg03811012010-12-08 00:21:06 +02002688 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +02002689 case MGMT_OP_READ_VERSION:
2690 err = read_version(sk);
2691 break;
Johan Hedberge70bb2e2012-02-13 16:59:33 +02002692 case MGMT_OP_READ_COMMANDS:
2693 err = read_commands(sk);
2694 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +02002695 case MGMT_OP_READ_INDEX_LIST:
2696 err = read_index_list(sk);
2697 break;
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002698 case MGMT_OP_READ_INFO:
Szymon Janc4e51eae2011-02-25 19:05:48 +01002699 err = read_controller_info(sk, index);
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002700 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002701 case MGMT_OP_SET_POWERED:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002702 err = set_powered(sk, index, cp, len);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002703 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +02002704 case MGMT_OP_SET_DISCOVERABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002705 err = set_discoverable(sk, index, cp, len);
Johan Hedberg73f22f62010-12-29 16:00:25 +02002706 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002707 case MGMT_OP_SET_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002708 err = set_connectable(sk, index, cp, len);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002709 break;
Johan Hedbergf7c68692011-12-15 00:47:36 +02002710 case MGMT_OP_SET_FAST_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002711 err = set_fast_connectable(sk, index, cp, len);
Johan Hedbergf7c68692011-12-15 00:47:36 +02002712 break;
Johan Hedbergc542a062011-01-26 13:11:03 +02002713 case MGMT_OP_SET_PAIRABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002714 err = set_pairable(sk, index, cp, len);
Johan Hedbergc542a062011-01-26 13:11:03 +02002715 break;
Johan Hedberg33ef95e2012-02-16 23:56:27 +02002716 case MGMT_OP_SET_LINK_SECURITY:
2717 err = set_link_security(sk, index, cp, len);
2718 break;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02002719 case MGMT_OP_SET_SSP:
2720 err = set_ssp(sk, index, cp, len);
2721 break;
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02002722 case MGMT_OP_SET_HS:
2723 err = set_hs(sk, index, cp, len);
2724 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002725 case MGMT_OP_ADD_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002726 err = add_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002727 break;
2728 case MGMT_OP_REMOVE_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002729 err = remove_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002730 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002731 case MGMT_OP_SET_DEV_CLASS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002732 err = set_dev_class(sk, index, cp, len);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002733 break;
Johan Hedberg86742e12011-11-07 23:13:38 +02002734 case MGMT_OP_LOAD_LINK_KEYS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002735 err = load_link_keys(sk, index, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002736 break;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002737 case MGMT_OP_DISCONNECT:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002738 err = disconnect(sk, index, cp, len);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002739 break;
Johan Hedberg2784eb42011-01-21 13:56:35 +02002740 case MGMT_OP_GET_CONNECTIONS:
Szymon Janc8ce62842011-03-01 16:55:32 +01002741 err = get_connections(sk, index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02002742 break;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002743 case MGMT_OP_PIN_CODE_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002744 err = pin_code_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002745 break;
2746 case MGMT_OP_PIN_CODE_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002747 err = pin_code_neg_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002748 break;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002749 case MGMT_OP_SET_IO_CAPABILITY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002750 err = set_io_capability(sk, index, cp, len);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002751 break;
Johan Hedberge9a416b2011-02-19 12:05:56 -03002752 case MGMT_OP_PAIR_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002753 err = pair_device(sk, index, cp, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002754 break;
Johan Hedberg28424702012-02-02 04:02:29 +02002755 case MGMT_OP_CANCEL_PAIR_DEVICE:
2756 err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len);
2757 break;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002758 case MGMT_OP_UNPAIR_DEVICE:
2759 err = unpair_device(sk, index, cp, len);
2760 break;
Johan Hedberga5c29682011-02-19 12:05:57 -03002761 case MGMT_OP_USER_CONFIRM_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002762 err = user_confirm_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002763 break;
2764 case MGMT_OP_USER_CONFIRM_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002765 err = user_confirm_neg_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002766 break;
Brian Gix604086b2011-11-23 08:28:33 -08002767 case MGMT_OP_USER_PASSKEY_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002768 err = user_passkey_reply(sk, index, cp, len);
Brian Gix604086b2011-11-23 08:28:33 -08002769 break;
2770 case MGMT_OP_USER_PASSKEY_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002771 err = user_passkey_neg_reply(sk, index, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002772 break;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002773 case MGMT_OP_SET_LOCAL_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002774 err = set_local_name(sk, index, cp, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002775 break;
Szymon Jancc35938b2011-03-22 13:12:21 +01002776 case MGMT_OP_READ_LOCAL_OOB_DATA:
2777 err = read_local_oob_data(sk, index);
2778 break;
Szymon Janc2763eda2011-03-22 13:12:22 +01002779 case MGMT_OP_ADD_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002780 err = add_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002781 break;
2782 case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002783 err = remove_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002784 break;
Johan Hedberg14a53662011-04-27 10:29:56 -04002785 case MGMT_OP_START_DISCOVERY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002786 err = start_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002787 break;
2788 case MGMT_OP_STOP_DISCOVERY:
Johan Hedbergd9306502012-02-20 23:25:18 +02002789 err = stop_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002790 break;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002791 case MGMT_OP_CONFIRM_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002792 err = confirm_name(sk, index, cp, len);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002793 break;
Antti Julku7fbec222011-06-15 12:01:15 +03002794 case MGMT_OP_BLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002795 err = block_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002796 break;
2797 case MGMT_OP_UNBLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002798 err = unblock_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002799 break;
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002800 case MGMT_OP_LOAD_LONG_TERM_KEYS:
2801 err = load_long_term_keys(sk, index, cp, len);
2802 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002803 default:
Johan Hedberg72a734e2010-12-30 00:38:22 +02002804 BT_DBG("Unknown op %u", opcode);
Johan Hedbergca69b792011-11-11 18:10:00 +02002805 err = cmd_status(sk, index, opcode,
2806 MGMT_STATUS_UNKNOWN_COMMAND);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002807 break;
2808 }
Johan Hedberg72a734e2010-12-30 00:38:22 +02002809
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002810 if (err < 0)
Johan Hedberg72a734e2010-12-30 00:38:22 +02002811 goto done;
2812
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002813 err = msglen;
2814
2815done:
2816 kfree(buf);
2817 return err;
2818}
2819
Johan Hedbergb24752f2011-11-03 14:40:33 +02002820static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
2821{
2822 u8 *status = data;
2823
2824 cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
2825 mgmt_pending_remove(cmd);
2826}
2827
Johan Hedberg744cf192011-11-08 20:40:14 +02002828int mgmt_index_added(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002829{
Johan Hedberg744cf192011-11-08 20:40:14 +02002830 return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002831}
2832
Johan Hedberg744cf192011-11-08 20:40:14 +02002833int mgmt_index_removed(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002834{
Johan Hedbergb24752f2011-11-03 14:40:33 +02002835 u8 status = ENODEV;
2836
Johan Hedberg744cf192011-11-08 20:40:14 +02002837 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002838
Johan Hedberg744cf192011-11-08 20:40:14 +02002839 return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002840}
2841
2842struct cmd_lookup {
Johan Hedberg03811012010-12-08 00:21:06 +02002843 struct sock *sk;
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002844 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +02002845};
2846
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002847static void settings_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberg03811012010-12-08 00:21:06 +02002848{
Johan Hedberg03811012010-12-08 00:21:06 +02002849 struct cmd_lookup *match = data;
2850
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002851 send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
Johan Hedberg03811012010-12-08 00:21:06 +02002852
2853 list_del(&cmd->list);
2854
2855 if (match->sk == NULL) {
2856 match->sk = cmd->sk;
2857 sock_hold(match->sk);
2858 }
2859
2860 mgmt_pending_free(cmd);
2861}
2862
Johan Hedberg744cf192011-11-08 20:40:14 +02002863int mgmt_powered(struct hci_dev *hdev, u8 powered)
Johan Hedberg03811012010-12-08 00:21:06 +02002864{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002865 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002866 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002867 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002868
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002869 mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002870
Johan Hedbergb24752f2011-11-03 14:40:33 +02002871 if (!powered) {
2872 u8 status = ENETDOWN;
Johan Hedberg744cf192011-11-08 20:40:14 +02002873 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002874 }
2875
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002876 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002877
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002878 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002879 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002880
2881 if (match.sk)
2882 sock_put(match.sk);
2883
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002884 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002885}
2886
Johan Hedberg744cf192011-11-08 20:40:14 +02002887int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
Johan Hedberg03811012010-12-08 00:21:06 +02002888{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002889 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002890 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002891 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002892
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002893 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002894
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002895 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002896
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002897 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002898 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002899 if (match.sk)
2900 sock_put(match.sk);
2901
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002902 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002903}
2904
Johan Hedberg744cf192011-11-08 20:40:14 +02002905int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
Johan Hedberg03811012010-12-08 00:21:06 +02002906{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002907 __le32 ev;
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002908 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002909 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002910
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002911 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
2912 &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002913
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002914 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002915
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002916 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002917
2918 if (match.sk)
2919 sock_put(match.sk);
2920
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002921 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002922}
2923
Johan Hedberg744cf192011-11-08 20:40:14 +02002924int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002925{
Johan Hedbergca69b792011-11-11 18:10:00 +02002926 u8 mgmt_err = mgmt_status(status);
2927
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002928 if (scan & SCAN_PAGE)
Johan Hedberg744cf192011-11-08 20:40:14 +02002929 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002930 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002931
2932 if (scan & SCAN_INQUIRY)
Johan Hedberg744cf192011-11-08 20:40:14 +02002933 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002934 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002935
2936 return 0;
2937}
2938
Johan Hedberg744cf192011-11-08 20:40:14 +02002939int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
2940 u8 persistent)
Johan Hedberg03811012010-12-08 00:21:06 +02002941{
Johan Hedberg86742e12011-11-07 23:13:38 +02002942 struct mgmt_ev_new_link_key ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002943
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002944 memset(&ev, 0, sizeof(ev));
Johan Hedberg03811012010-12-08 00:21:06 +02002945
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002946 ev.store_hint = persistent;
Johan Hedbergd753fdc2012-02-17 14:06:34 +02002947 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2948 ev.key.addr.type = MGMT_ADDR_BREDR;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002949 ev.key.type = key->type;
2950 memcpy(ev.key.val, key->val, 16);
2951 ev.key.pin_len = key->pin_len;
Johan Hedberg03811012010-12-08 00:21:06 +02002952
Johan Hedberg744cf192011-11-08 20:40:14 +02002953 return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002954}
Johan Hedbergf7520542011-01-20 12:34:39 +02002955
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002956int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
2957{
2958 struct mgmt_ev_new_long_term_key ev;
2959
2960 memset(&ev, 0, sizeof(ev));
2961
2962 ev.store_hint = persistent;
2963 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2964 ev.key.addr.type = key->bdaddr_type;
2965 ev.key.authenticated = key->authenticated;
2966 ev.key.enc_size = key->enc_size;
2967 ev.key.ediv = key->ediv;
2968
2969 if (key->type == HCI_SMP_LTK)
2970 ev.key.master = 1;
2971
2972 memcpy(ev.key.rand, key->rand, sizeof(key->rand));
2973 memcpy(ev.key.val, key->val, sizeof(key->val));
2974
2975 return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
2976 &ev, sizeof(ev), NULL);
2977}
2978
Johan Hedbergafc747a2012-01-15 18:11:07 +02002979int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedbergb644ba32012-01-17 21:48:47 +02002980 u8 addr_type, u8 *name, u8 name_len,
2981 u8 *dev_class)
Johan Hedbergf7520542011-01-20 12:34:39 +02002982{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002983 char buf[512];
2984 struct mgmt_ev_device_connected *ev = (void *) buf;
2985 u16 eir_len = 0;
Johan Hedbergf7520542011-01-20 12:34:39 +02002986
Johan Hedbergb644ba32012-01-17 21:48:47 +02002987 bacpy(&ev->addr.bdaddr, bdaddr);
2988 ev->addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002989
Johan Hedbergb644ba32012-01-17 21:48:47 +02002990 if (name_len > 0)
2991 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
2992 name, name_len);
2993
2994 if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
2995 eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
2996 EIR_CLASS_OF_DEV, dev_class, 3);
2997
2998 put_unaligned_le16(eir_len, &ev->eir_len);
2999
3000 return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
3001 sizeof(*ev) + eir_len, NULL);
Johan Hedbergf7520542011-01-20 12:34:39 +02003002}
3003
Johan Hedberg8962ee72011-01-20 12:40:27 +02003004static void disconnect_rsp(struct pending_cmd *cmd, void *data)
3005{
Szymon Jancc68fb7f2011-03-22 13:12:19 +01003006 struct mgmt_cp_disconnect *cp = cmd->param;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003007 struct sock **sk = data;
Johan Hedberga38528f2011-01-22 06:46:43 +02003008 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003009
Johan Hedberg88c3df12012-02-09 14:27:38 +02003010 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
3011 rp.addr.type = cp->addr.type;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003012
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003013 cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
3014 sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02003015
3016 *sk = cmd->sk;
3017 sock_hold(*sk);
3018
Johan Hedberga664b5b2011-02-19 12:06:02 -03003019 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003020}
3021
Johan Hedberg124f6e32012-02-09 13:50:12 +02003022static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberga8a1d192011-11-10 15:54:38 +02003023{
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003024 struct hci_dev *hdev = data;
Johan Hedberg124f6e32012-02-09 13:50:12 +02003025 struct mgmt_cp_unpair_device *cp = cmd->param;
3026 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02003027
3028 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02003029 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
3030 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02003031
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003032 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
3033
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003034 cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02003035
3036 mgmt_pending_remove(cmd);
3037}
3038
Johan Hedbergafc747a2012-01-15 18:11:07 +02003039int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
3040 u8 link_type, u8 addr_type)
Johan Hedbergf7520542011-01-20 12:34:39 +02003041{
Johan Hedberg4c659c32011-11-07 23:13:39 +02003042 struct mgmt_addr_info ev;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003043 struct sock *sk = NULL;
3044 int err;
3045
Johan Hedberg744cf192011-11-08 20:40:14 +02003046 mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
Johan Hedbergf7520542011-01-20 12:34:39 +02003047
Johan Hedbergf7520542011-01-20 12:34:39 +02003048 bacpy(&ev.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02003049 ev.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02003050
Johan Hedbergafc747a2012-01-15 18:11:07 +02003051 err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
3052 sk);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003053
3054 if (sk)
3055 sock_put(sk);
3056
Johan Hedberg124f6e32012-02-09 13:50:12 +02003057 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003058 hdev);
Johan Hedberga8a1d192011-11-10 15:54:38 +02003059
Johan Hedberg8962ee72011-01-20 12:40:27 +02003060 return err;
3061}
3062
Johan Hedberg88c3df12012-02-09 14:27:38 +02003063int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
3064 u8 link_type, u8 addr_type, u8 status)
Johan Hedberg8962ee72011-01-20 12:40:27 +02003065{
Johan Hedberg88c3df12012-02-09 14:27:38 +02003066 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003067 struct pending_cmd *cmd;
3068 int err;
3069
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003070 cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003071 if (!cmd)
3072 return -ENOENT;
3073
Johan Hedberg88c3df12012-02-09 14:27:38 +02003074 bacpy(&rp.addr.bdaddr, bdaddr);
3075 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg37d9ef72011-11-10 15:54:39 +02003076
Johan Hedberg88c3df12012-02-09 14:27:38 +02003077 err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003078 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02003079
Johan Hedberga664b5b2011-02-19 12:06:02 -03003080 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003081
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003082 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
3083 hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003084 return err;
Johan Hedbergf7520542011-01-20 12:34:39 +02003085}
Johan Hedberg17d5c042011-01-22 06:09:08 +02003086
Johan Hedberg48264f02011-11-09 13:58:58 +02003087int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3088 u8 addr_type, u8 status)
Johan Hedberg17d5c042011-01-22 06:09:08 +02003089{
3090 struct mgmt_ev_connect_failed ev;
3091
Johan Hedberg4c659c32011-11-07 23:13:39 +02003092 bacpy(&ev.addr.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02003093 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003094 ev.status = mgmt_status(status);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003095
Johan Hedberg744cf192011-11-08 20:40:14 +02003096 return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003097}
Johan Hedberg980e1a52011-01-22 06:10:07 +02003098
Johan Hedberg744cf192011-11-08 20:40:14 +02003099int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003100{
3101 struct mgmt_ev_pin_code_request ev;
3102
Johan Hedbergd8457692012-02-17 14:24:57 +02003103 bacpy(&ev.addr.bdaddr, bdaddr);
3104 ev.addr.type = MGMT_ADDR_BREDR;
Waldemar Rymarkiewicza770bb52011-04-28 12:07:59 +02003105 ev.secure = secure;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003106
Johan Hedberg744cf192011-11-08 20:40:14 +02003107 return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003108 NULL);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003109}
3110
Johan Hedberg744cf192011-11-08 20:40:14 +02003111int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3112 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003113{
3114 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003115 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003116 int err;
3117
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003118 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003119 if (!cmd)
3120 return -ENOENT;
3121
Johan Hedbergd8457692012-02-17 14:24:57 +02003122 bacpy(&rp.addr.bdaddr, bdaddr);
3123 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003124
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003125 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
3126 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003127
Johan Hedberga664b5b2011-02-19 12:06:02 -03003128 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003129
3130 return err;
3131}
3132
Johan Hedberg744cf192011-11-08 20:40:14 +02003133int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3134 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003135{
3136 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003137 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003138 int err;
3139
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003140 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003141 if (!cmd)
3142 return -ENOENT;
3143
Johan Hedbergd8457692012-02-17 14:24:57 +02003144 bacpy(&rp.addr.bdaddr, bdaddr);
3145 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003146
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003147 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
3148 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003149
Johan Hedberga664b5b2011-02-19 12:06:02 -03003150 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003151
3152 return err;
3153}
Johan Hedberga5c29682011-02-19 12:05:57 -03003154
Johan Hedberg744cf192011-11-08 20:40:14 +02003155int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003156 u8 link_type, u8 addr_type, __le32 value,
3157 u8 confirm_hint)
Johan Hedberga5c29682011-02-19 12:05:57 -03003158{
3159 struct mgmt_ev_user_confirm_request ev;
3160
Johan Hedberg744cf192011-11-08 20:40:14 +02003161 BT_DBG("%s", hdev->name);
Johan Hedberga5c29682011-02-19 12:05:57 -03003162
Johan Hedberg272d90d2012-02-09 15:26:12 +02003163 bacpy(&ev.addr.bdaddr, bdaddr);
3164 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg55bc1a32011-04-28 11:28:56 -07003165 ev.confirm_hint = confirm_hint;
Johan Hedberga5c29682011-02-19 12:05:57 -03003166 put_unaligned_le32(value, &ev.value);
3167
Johan Hedberg744cf192011-11-08 20:40:14 +02003168 return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003169 NULL);
Johan Hedberga5c29682011-02-19 12:05:57 -03003170}
3171
Johan Hedberg272d90d2012-02-09 15:26:12 +02003172int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
3173 u8 link_type, u8 addr_type)
Brian Gix604086b2011-11-23 08:28:33 -08003174{
3175 struct mgmt_ev_user_passkey_request ev;
3176
3177 BT_DBG("%s", hdev->name);
3178
Johan Hedberg272d90d2012-02-09 15:26:12 +02003179 bacpy(&ev.addr.bdaddr, bdaddr);
3180 ev.addr.type = link_to_mgmt(link_type, addr_type);
Brian Gix604086b2011-11-23 08:28:33 -08003181
3182 return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
3183 NULL);
3184}
3185
Brian Gix0df4c182011-11-16 13:53:13 -08003186static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003187 u8 link_type, u8 addr_type, u8 status,
3188 u8 opcode)
Johan Hedberga5c29682011-02-19 12:05:57 -03003189{
3190 struct pending_cmd *cmd;
3191 struct mgmt_rp_user_confirm_reply rp;
3192 int err;
3193
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003194 cmd = mgmt_pending_find(opcode, hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03003195 if (!cmd)
3196 return -ENOENT;
3197
Johan Hedberg272d90d2012-02-09 15:26:12 +02003198 bacpy(&rp.addr.bdaddr, bdaddr);
3199 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003200 err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
3201 &rp, sizeof(rp));
Johan Hedberga5c29682011-02-19 12:05:57 -03003202
Johan Hedberga664b5b2011-02-19 12:06:02 -03003203 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03003204
3205 return err;
3206}
3207
Johan Hedberg744cf192011-11-08 20:40:14 +02003208int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003209 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003210{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003211 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3212 status, MGMT_OP_USER_CONFIRM_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003213}
3214
Johan Hedberg272d90d2012-02-09 15:26:12 +02003215int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3216 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003217{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003218 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3219 status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003220}
Johan Hedberg2a611692011-02-19 12:06:00 -03003221
Brian Gix604086b2011-11-23 08:28:33 -08003222int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003223 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003224{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003225 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3226 status, MGMT_OP_USER_PASSKEY_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003227}
3228
Johan Hedberg272d90d2012-02-09 15:26:12 +02003229int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3230 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003231{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003232 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3233 status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003234}
3235
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003236int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3237 u8 addr_type, u8 status)
Johan Hedberg2a611692011-02-19 12:06:00 -03003238{
3239 struct mgmt_ev_auth_failed ev;
3240
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003241 bacpy(&ev.addr.bdaddr, bdaddr);
3242 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003243 ev.status = mgmt_status(status);
Johan Hedberg2a611692011-02-19 12:06:00 -03003244
Johan Hedberg744cf192011-11-08 20:40:14 +02003245 return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg2a611692011-02-19 12:06:00 -03003246}
Johan Hedbergb312b1612011-03-16 14:29:37 +02003247
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003248int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
3249{
3250 struct cmd_lookup match = { NULL, hdev };
3251 __le32 ev;
3252 int err;
3253
3254 if (status) {
3255 u8 mgmt_err = mgmt_status(status);
3256 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
3257 cmd_status_rsp, &mgmt_err);
3258 return 0;
3259 }
3260
3261 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
3262 &match);
3263
3264 ev = cpu_to_le32(get_current_settings(hdev));
3265 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3266
3267 if (match.sk)
3268 sock_put(match.sk);
3269
3270 return err;
3271}
3272
Johan Hedbergcacaf522012-02-21 00:52:42 +02003273static int clear_eir(struct hci_dev *hdev)
3274{
3275 struct hci_cp_write_eir cp;
3276
3277 if (!(hdev->features[6] & LMP_EXT_INQ))
3278 return 0;
3279
3280 memset(&cp, 0, sizeof(cp));
3281
3282 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
3283}
3284
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003285int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status)
3286{
3287 struct cmd_lookup match = { NULL, hdev };
3288 __le32 ev;
3289 int err;
3290
3291 if (status) {
3292 u8 mgmt_err = mgmt_status(status);
3293 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev,
3294 cmd_status_rsp, &mgmt_err);
3295 return 0;
3296 }
3297
3298 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
3299
3300 ev = cpu_to_le32(get_current_settings(hdev));
3301 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3302
Johan Hedbergcacaf522012-02-21 00:52:42 +02003303 if (match.sk) {
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003304 sock_put(match.sk);
3305
Johan Hedbergcacaf522012-02-21 00:52:42 +02003306 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
3307 update_eir(hdev);
3308 else
3309 clear_eir(hdev);
3310 }
3311
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003312 return err;
3313}
3314
Johan Hedberg744cf192011-11-08 20:40:14 +02003315int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
Johan Hedbergb312b1612011-03-16 14:29:37 +02003316{
3317 struct pending_cmd *cmd;
3318 struct mgmt_cp_set_local_name ev;
3319 int err;
3320
3321 memset(&ev, 0, sizeof(ev));
3322 memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
3323
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003324 cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02003325 if (!cmd)
3326 goto send_event;
3327
3328 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003329 err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02003330 mgmt_status(status));
Johan Hedbergb312b1612011-03-16 14:29:37 +02003331 goto failed;
3332 }
3333
Johan Hedberg744cf192011-11-08 20:40:14 +02003334 update_eir(hdev);
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03003335
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003336 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
Johan Hedbergb312b1612011-03-16 14:29:37 +02003337 sizeof(ev));
3338 if (err < 0)
3339 goto failed;
3340
3341send_event:
Johan Hedberg744cf192011-11-08 20:40:14 +02003342 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
Johan Hedbergb312b1612011-03-16 14:29:37 +02003343 cmd ? cmd->sk : NULL);
3344
3345failed:
3346 if (cmd)
3347 mgmt_pending_remove(cmd);
3348 return err;
3349}
Szymon Jancc35938b2011-03-22 13:12:21 +01003350
Johan Hedberg744cf192011-11-08 20:40:14 +02003351int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
3352 u8 *randomizer, u8 status)
Szymon Jancc35938b2011-03-22 13:12:21 +01003353{
3354 struct pending_cmd *cmd;
3355 int err;
3356
Johan Hedberg744cf192011-11-08 20:40:14 +02003357 BT_DBG("%s status %u", hdev->name, status);
Szymon Jancc35938b2011-03-22 13:12:21 +01003358
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003359 cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01003360 if (!cmd)
3361 return -ENOENT;
3362
3363 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003364 err = cmd_status(cmd->sk, hdev->id,
Johan Hedbergca69b792011-11-11 18:10:00 +02003365 MGMT_OP_READ_LOCAL_OOB_DATA,
3366 mgmt_status(status));
Szymon Jancc35938b2011-03-22 13:12:21 +01003367 } else {
3368 struct mgmt_rp_read_local_oob_data rp;
3369
3370 memcpy(rp.hash, hash, sizeof(rp.hash));
3371 memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
3372
Johan Hedberg744cf192011-11-08 20:40:14 +02003373 err = cmd_complete(cmd->sk, hdev->id,
3374 MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003375 0, &rp, sizeof(rp));
Szymon Jancc35938b2011-03-22 13:12:21 +01003376 }
3377
3378 mgmt_pending_remove(cmd);
3379
3380 return err;
3381}
Johan Hedberge17acd42011-03-30 23:57:16 +03003382
Johan Hedberg48264f02011-11-09 13:58:58 +02003383int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg561aafb2012-01-04 13:31:59 +02003384 u8 addr_type, u8 *dev_class, s8 rssi,
Johan Hedberge319d2e2012-01-15 19:51:59 +02003385 u8 cfm_name, u8 *eir, u16 eir_len)
Johan Hedberge17acd42011-03-30 23:57:16 +03003386{
Johan Hedberge319d2e2012-01-15 19:51:59 +02003387 char buf[512];
3388 struct mgmt_ev_device_found *ev = (void *) buf;
Johan Hedberg1dc06092012-01-15 21:01:23 +02003389 size_t ev_size;
Johan Hedberge17acd42011-03-30 23:57:16 +03003390
Johan Hedberg1dc06092012-01-15 21:01:23 +02003391 /* Leave 5 bytes for a potential CoD field */
3392 if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
Andre Guedes7d262f82012-01-10 18:20:49 -03003393 return -EINVAL;
3394
Johan Hedberg1dc06092012-01-15 21:01:23 +02003395 memset(buf, 0, sizeof(buf));
3396
Johan Hedberge319d2e2012-01-15 19:51:59 +02003397 bacpy(&ev->addr.bdaddr, bdaddr);
3398 ev->addr.type = link_to_mgmt(link_type, addr_type);
3399 ev->rssi = rssi;
3400 ev->confirm_name = cfm_name;
Johan Hedberge17acd42011-03-30 23:57:16 +03003401
Johan Hedberg1dc06092012-01-15 21:01:23 +02003402 if (eir_len > 0)
Johan Hedberge319d2e2012-01-15 19:51:59 +02003403 memcpy(ev->eir, eir, eir_len);
Johan Hedberge17acd42011-03-30 23:57:16 +03003404
Johan Hedberg1dc06092012-01-15 21:01:23 +02003405 if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
3406 eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
3407 dev_class, 3);
3408
3409 put_unaligned_le16(eir_len, &ev->eir_len);
3410
3411 ev_size = sizeof(*ev) + eir_len;
Andre Guedesf8523592011-09-09 18:56:26 -03003412
Johan Hedberge319d2e2012-01-15 19:51:59 +02003413 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
Johan Hedberge17acd42011-03-30 23:57:16 +03003414}
Johan Hedberga88a9652011-03-30 13:18:12 +03003415
Johan Hedbergb644ba32012-01-17 21:48:47 +02003416int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3417 u8 addr_type, s8 rssi, u8 *name, u8 name_len)
Johan Hedberga88a9652011-03-30 13:18:12 +03003418{
Johan Hedbergb644ba32012-01-17 21:48:47 +02003419 struct mgmt_ev_device_found *ev;
3420 char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
3421 u16 eir_len;
Johan Hedberga88a9652011-03-30 13:18:12 +03003422
Johan Hedbergb644ba32012-01-17 21:48:47 +02003423 ev = (struct mgmt_ev_device_found *) buf;
Johan Hedberga88a9652011-03-30 13:18:12 +03003424
Johan Hedbergb644ba32012-01-17 21:48:47 +02003425 memset(buf, 0, sizeof(buf));
Johan Hedberga88a9652011-03-30 13:18:12 +03003426
Johan Hedbergb644ba32012-01-17 21:48:47 +02003427 bacpy(&ev->addr.bdaddr, bdaddr);
3428 ev->addr.type = link_to_mgmt(link_type, addr_type);
3429 ev->rssi = rssi;
3430
3431 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
3432 name_len);
3433
3434 put_unaligned_le16(eir_len, &ev->eir_len);
3435
Johan Hedberg053c7e02012-02-04 00:06:00 +02003436 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
3437 sizeof(*ev) + eir_len, NULL);
Johan Hedberga88a9652011-03-30 13:18:12 +03003438}
Johan Hedberg314b2382011-04-27 10:29:57 -04003439
Andre Guedes7a135102011-11-09 17:14:25 -03003440int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
Johan Hedberg164a6e72011-11-01 17:06:44 +02003441{
3442 struct pending_cmd *cmd;
Johan Hedbergf808e162012-02-19 12:52:07 +02003443 u8 type;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003444 int err;
3445
Andre Guedes203159d2012-02-13 15:41:01 -03003446 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
3447
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003448 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003449 if (!cmd)
3450 return -ENOENT;
3451
Johan Hedbergf808e162012-02-19 12:52:07 +02003452 type = hdev->discovery.type;
3453
3454 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3455 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003456 mgmt_pending_remove(cmd);
3457
3458 return err;
3459}
3460
Andre Guedese6d465c2011-11-09 17:14:26 -03003461int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
3462{
3463 struct pending_cmd *cmd;
3464 int err;
3465
3466 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
3467 if (!cmd)
3468 return -ENOENT;
3469
Johan Hedbergd9306502012-02-20 23:25:18 +02003470 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3471 &hdev->discovery.type,
3472 sizeof(hdev->discovery.type));
Johan Hedberg03811012010-12-08 00:21:06 +02003473 mgmt_pending_remove(cmd);
3474
3475 return err;
3476}
Johan Hedberg314b2382011-04-27 10:29:57 -04003477
Johan Hedberg744cf192011-11-08 20:40:14 +02003478int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
Johan Hedberg314b2382011-04-27 10:29:57 -04003479{
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003480 struct mgmt_ev_discovering ev;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003481 struct pending_cmd *cmd;
3482
Andre Guedes343fb142011-11-22 17:14:19 -03003483 BT_DBG("%s discovering %u", hdev->name, discovering);
3484
Johan Hedberg164a6e72011-11-01 17:06:44 +02003485 if (discovering)
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003486 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003487 else
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003488 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003489
3490 if (cmd != NULL) {
Johan Hedbergf808e162012-02-19 12:52:07 +02003491 u8 type = hdev->discovery.type;
3492
Johan Hedbergd9306502012-02-20 23:25:18 +02003493 cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0,
Johan Hedbergf808e162012-02-19 12:52:07 +02003494 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003495 mgmt_pending_remove(cmd);
3496 }
3497
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003498 memset(&ev, 0, sizeof(ev));
3499 ev.type = hdev->discovery.type;
3500 ev.discovering = discovering;
3501
3502 return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg314b2382011-04-27 10:29:57 -04003503}
Antti Julku5e762442011-08-25 16:48:02 +03003504
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003505int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003506{
3507 struct pending_cmd *cmd;
3508 struct mgmt_ev_device_blocked ev;
3509
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003510 cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003511
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003512 bacpy(&ev.addr.bdaddr, bdaddr);
3513 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003514
Johan Hedberg744cf192011-11-08 20:40:14 +02003515 return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
3516 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003517}
3518
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003519int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003520{
3521 struct pending_cmd *cmd;
3522 struct mgmt_ev_device_unblocked ev;
3523
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003524 cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003525
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003526 bacpy(&ev.addr.bdaddr, bdaddr);
3527 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003528
Johan Hedberg744cf192011-11-08 20:40:14 +02003529 return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
3530 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003531}
Marcel Holtmannd7b7e792012-02-20 21:47:49 +01003532
3533module_param(enable_hs, bool, 0644);
3534MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
3535
3536module_param(enable_le, bool, 0644);
3537MODULE_PARM_DESC(enable_le, "Enable Low Energy support");