blob: 0f87030f9c3041766e5ede30c2ae0d9b95ec0285 [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 Hedberg0cbf4ed62012-02-21 17:25:22 +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 Hedberg0cbf4ed62012-02-21 17:25:22 +0200610 /* Non-mgmt controlled devices get this bit set
611 * implicitly so that pairing works for them, however
612 * for mgmt we require user-space to explicitly enable
613 * it
614 */
615 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
616 }
617
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200618 if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200619 schedule_delayed_work(&hdev->service_cache,
620 msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
621}
622
Johan Hedberg03811012010-12-08 00:21:06 +0200623static int read_controller_info(struct sock *sk, u16 index)
624{
625 struct mgmt_rp_read_info rp;
626 struct hci_dev *hdev;
627
628 BT_DBG("sock %p hci%u", sk, index);
629
630 hdev = hci_dev_get(index);
631 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200632 return cmd_status(sk, index, MGMT_OP_READ_INFO,
633 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200634
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300635 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200636
Johan Hedberg7d785252011-12-15 00:47:39 +0200637 if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
638 mgmt_init_hdev(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200639
640 memset(&rp, 0, sizeof(rp));
641
Johan Hedberg03811012010-12-08 00:21:06 +0200642 bacpy(&rp.bdaddr, &hdev->bdaddr);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200643
644 rp.version = hdev->hci_ver;
645
Johan Hedberg03811012010-12-08 00:21:06 +0200646 put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200647
648 rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
649 rp.current_settings = cpu_to_le32(get_current_settings(hdev));
650
651 memcpy(rp.dev_class, hdev->dev_class, 3);
Johan Hedberg03811012010-12-08 00:21:06 +0200652
653 memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
654
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300655 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200656 hci_dev_put(hdev);
657
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200658 return cmd_complete(sk, index, MGMT_OP_READ_INFO, 0, &rp, sizeof(rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200659}
660
661static void mgmt_pending_free(struct pending_cmd *cmd)
662{
663 sock_put(cmd->sk);
664 kfree(cmd->param);
665 kfree(cmd);
666}
667
668static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
669 struct hci_dev *hdev,
670 void *data, u16 len)
671{
672 struct pending_cmd *cmd;
673
674 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
675 if (!cmd)
676 return NULL;
677
678 cmd->opcode = opcode;
679 cmd->index = hdev->id;
680
681 cmd->param = kmalloc(len, GFP_ATOMIC);
682 if (!cmd->param) {
683 kfree(cmd);
684 return NULL;
685 }
686
687 if (data)
688 memcpy(cmd->param, data, len);
689
690 cmd->sk = sk;
691 sock_hold(sk);
692
693 list_add(&cmd->list, &hdev->mgmt_pending);
694
695 return cmd;
696}
697
698static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
699 void (*cb)(struct pending_cmd *cmd, void *data),
700 void *data)
701{
702 struct list_head *p, *n;
703
704 list_for_each_safe(p, n, &hdev->mgmt_pending) {
705 struct pending_cmd *cmd;
706
707 cmd = list_entry(p, struct pending_cmd, list);
708
709 if (opcode > 0 && cmd->opcode != opcode)
710 continue;
711
712 cb(cmd, data);
713 }
714}
715
716static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
717{
718 struct pending_cmd *cmd;
719
720 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
721 if (cmd->opcode == opcode)
722 return cmd;
723 }
724
725 return NULL;
726}
727
728static void mgmt_pending_remove(struct pending_cmd *cmd)
729{
730 list_del(&cmd->list);
731 mgmt_pending_free(cmd);
732}
733
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200734static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
Johan Hedberg86805702011-11-11 16:18:52 +0200735{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200736 __le32 settings = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg86805702011-11-11 16:18:52 +0200737
Johan Hedbergaee9b2182012-02-18 15:07:59 +0200738 return cmd_complete(sk, hdev->id, opcode, 0, &settings,
739 sizeof(settings));
Johan Hedberg86805702011-11-11 16:18:52 +0200740}
741
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300742static int set_powered(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200743{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300744 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200745 struct hci_dev *hdev;
746 struct pending_cmd *cmd;
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200747 int err;
Johan Hedberg03811012010-12-08 00:21:06 +0200748
Johan Hedberg03811012010-12-08 00:21:06 +0200749 BT_DBG("request for hci%u", index);
750
751 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200752 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
753 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200754
755 hdev = hci_dev_get(index);
756 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200757 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
758 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200759
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300760 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200761
Marcel Holtmannf0d4b782012-02-21 12:14:25 +0100762 if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
763 cancel_delayed_work(&hdev->power_off);
764
765 if (cp->val) {
766 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
767 mgmt_powered(hdev, 1);
768 goto failed;
769 }
770 }
771
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200772 if (!!cp->val == hdev_is_powered(hdev)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200773 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200774 goto failed;
775 }
776
777 if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200778 err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
779 MGMT_STATUS_BUSY);
Johan Hedberg03811012010-12-08 00:21:06 +0200780 goto failed;
781 }
782
783 cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
784 if (!cmd) {
785 err = -ENOMEM;
786 goto failed;
787 }
788
789 if (cp->val)
Gustavo F. Padovan7f971042011-12-18 12:40:32 -0200790 schedule_work(&hdev->power_on);
Johan Hedberg03811012010-12-08 00:21:06 +0200791 else
Gustavo F. Padovan80b7ab32011-12-17 14:52:27 -0200792 schedule_work(&hdev->power_off.work);
Johan Hedberg03811012010-12-08 00:21:06 +0200793
794 err = 0;
795
796failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300797 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200798 hci_dev_put(hdev);
799 return err;
800}
801
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300802static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200803{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300804 struct mgmt_cp_set_discoverable *cp = data;
Johan Hedberge41d8b42010-12-13 21:07:03 +0200805 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +0200806 struct pending_cmd *cmd;
807 u8 scan;
808 int err;
809
Johan Hedberg03811012010-12-08 00:21:06 +0200810 BT_DBG("request for hci%u", index);
811
812 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200813 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
814 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200815
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200816 hdev = hci_dev_get(index);
817 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200818 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
819 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200820
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300821 hci_dev_lock(hdev);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200822
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200823 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200824 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
825 MGMT_STATUS_NOT_POWERED);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200826 goto failed;
827 }
828
829 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
830 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200831 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
832 MGMT_STATUS_BUSY);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200833 goto failed;
834 }
835
836 if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
837 test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200838 err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200839 goto failed;
840 }
841
842 cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
843 if (!cmd) {
844 err = -ENOMEM;
845 goto failed;
846 }
847
848 scan = SCAN_PAGE;
849
850 if (cp->val)
851 scan |= SCAN_INQUIRY;
852 else
853 cancel_delayed_work(&hdev->discov_off);
854
855 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
856 if (err < 0)
857 mgmt_pending_remove(cmd);
858
Johan Hedberg03811012010-12-08 00:21:06 +0200859 if (cp->val)
860 hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
861
Johan Hedberge41d8b42010-12-13 21:07:03 +0200862failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300863 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200864 hci_dev_put(hdev);
865
866 return err;
867}
868
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300869static int set_connectable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200870{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300871 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200872 struct hci_dev *hdev;
873 struct pending_cmd *cmd;
874 u8 scan;
875 int err;
876
Johan Hedberge41d8b42010-12-13 21:07:03 +0200877 BT_DBG("request for hci%u", index);
878
Johan Hedberg03811012010-12-08 00:21:06 +0200879 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200880 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
881 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200882
883 hdev = hci_dev_get(index);
884 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200885 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
886 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200887
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300888 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200889
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200890 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200891 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
892 MGMT_STATUS_NOT_POWERED);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200893 goto failed;
894 }
895
896 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
897 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200898 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
899 MGMT_STATUS_BUSY);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200900 goto failed;
901 }
902
903 if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200904 err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200905 goto failed;
906 }
907
908 cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
909 if (!cmd) {
910 err = -ENOMEM;
911 goto failed;
912 }
913
914 if (cp->val)
915 scan = SCAN_PAGE;
916 else
917 scan = 0;
918
919 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
920 if (err < 0)
921 mgmt_pending_remove(cmd);
922
923failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300924 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200925 hci_dev_put(hdev);
926
927 return err;
928}
929
930static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
931 u16 data_len, struct sock *skip_sk)
932{
933 struct sk_buff *skb;
934 struct mgmt_hdr *hdr;
935
936 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
937 if (!skb)
938 return -ENOMEM;
939
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200940 hdr = (void *) skb_put(skb, sizeof(*hdr));
941 hdr->opcode = cpu_to_le16(event);
942 if (hdev)
943 hdr->index = cpu_to_le16(hdev->id);
944 else
945 hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
946 hdr->len = cpu_to_le16(data_len);
947
948 if (data)
949 memcpy(skb_put(skb, data_len), data, data_len);
950
Marcel Holtmann470fe1b2012-02-20 14:50:30 +0100951 hci_send_to_control(skb, skip_sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200952 kfree_skb(skb);
953
954 return 0;
955}
956
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300957static int set_pairable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg73f22f62010-12-29 16:00:25 +0200958{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300959 struct mgmt_mode *cp = data;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200960 struct hci_dev *hdev;
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200961 __le32 ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200962 int err;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200963
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200964 BT_DBG("request for hci%u", index);
965
966 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200967 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
968 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200969
970 hdev = hci_dev_get(index);
971 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200972 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
973 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200974
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300975 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200976
977 if (cp->val)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200978 set_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200979 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200980 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200981
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200982 err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200983 if (err < 0)
984 goto failed;
985
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200986 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200987
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200988 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200989
990failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300991 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200992 hci_dev_put(hdev);
993
994 return err;
995}
Johan Hedberg72a734e2010-12-30 00:38:22 +0200996
Johan Hedberg33ef95e2012-02-16 23:56:27 +0200997static int set_link_security(struct sock *sk, u16 index, void *data, u16 len)
998{
999 struct mgmt_mode *cp = data;
1000 struct pending_cmd *cmd;
1001 struct hci_dev *hdev;
1002 uint8_t val;
1003 int err;
1004
1005 BT_DBG("request for hci%u", index);
1006
1007 if (len != sizeof(*cp))
1008 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1009 MGMT_STATUS_INVALID_PARAMS);
1010
1011 hdev = hci_dev_get(index);
1012 if (!hdev)
1013 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1014 MGMT_STATUS_INVALID_PARAMS);
1015
1016 hci_dev_lock(hdev);
1017
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001018 if (!hdev_is_powered(hdev)) {
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001019 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1020 MGMT_STATUS_NOT_POWERED);
1021 goto failed;
1022 }
1023
1024 if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
1025 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1026 MGMT_STATUS_BUSY);
1027 goto failed;
1028 }
1029
1030 val = !!cp->val;
1031
1032 if (test_bit(HCI_AUTH, &hdev->flags) == val) {
1033 err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
1034 goto failed;
1035 }
1036
1037 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len);
1038 if (!cmd) {
1039 err = -ENOMEM;
1040 goto failed;
1041 }
1042
1043 err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val);
1044 if (err < 0) {
1045 mgmt_pending_remove(cmd);
1046 goto failed;
1047 }
1048
1049failed:
1050 hci_dev_unlock(hdev);
1051 hci_dev_put(hdev);
1052
1053 return err;
1054}
1055
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001056static int set_ssp(struct sock *sk, u16 index, void *data, u16 len)
1057{
1058 struct mgmt_mode *cp = data;
1059 struct pending_cmd *cmd;
1060 struct hci_dev *hdev;
1061 uint8_t val;
1062 int err;
1063
1064 BT_DBG("request for hci%u", index);
1065
1066 if (len != sizeof(*cp))
1067 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1068 MGMT_STATUS_INVALID_PARAMS);
1069
1070 hdev = hci_dev_get(index);
1071 if (!hdev)
1072 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1073 MGMT_STATUS_INVALID_PARAMS);
1074
1075 hci_dev_lock(hdev);
1076
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001077 if (!hdev_is_powered(hdev)) {
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001078 err = cmd_status(sk, index, MGMT_OP_SET_SSP,
1079 MGMT_STATUS_NOT_POWERED);
1080 goto failed;
1081 }
1082
Johan Hedberg1e163572012-02-20 23:53:46 +02001083 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
1084 err = cmd_status(sk, index, MGMT_OP_SET_SSP,
1085 MGMT_STATUS_NOT_SUPPORTED);
1086 goto failed;
1087 }
1088
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001089 if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
1090 err = cmd_status(sk, index, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY);
1091 goto failed;
1092 }
1093
1094 val = !!cp->val;
1095
1096 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
1097 err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
1098 goto failed;
1099 }
1100
1101 cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len);
1102 if (!cmd) {
1103 err = -ENOMEM;
1104 goto failed;
1105 }
1106
1107 err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
1108 if (err < 0) {
1109 mgmt_pending_remove(cmd);
1110 goto failed;
1111 }
1112
1113failed:
1114 hci_dev_unlock(hdev);
1115 hci_dev_put(hdev);
1116
1117 return err;
1118}
1119
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001120static int set_hs(struct sock *sk, u16 index, void *data, u16 len)
1121{
1122 struct mgmt_mode *cp = data;
1123 struct hci_dev *hdev;
1124 int err;
1125
1126 BT_DBG("request for hci%u", index);
1127
1128 if (len != sizeof(*cp))
1129 return cmd_status(sk, index, MGMT_OP_SET_HS,
1130 MGMT_STATUS_INVALID_PARAMS);
1131
1132 hdev = hci_dev_get(index);
1133 if (!hdev)
1134 return cmd_status(sk, index, MGMT_OP_SET_HS,
1135 MGMT_STATUS_INVALID_PARAMS);
1136
1137 if (!enable_hs) {
1138 err = cmd_status(sk, index, MGMT_OP_SET_HS,
1139 MGMT_STATUS_NOT_SUPPORTED);
1140 goto failed;
1141 }
1142
1143 if (cp->val)
1144 set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
1145 else
1146 clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
1147
1148 err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
1149
1150failed:
1151 hci_dev_put(hdev);
1152 return err;
1153}
1154
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001155static int add_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001156{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001157 struct mgmt_cp_add_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001158 struct hci_dev *hdev;
1159 struct bt_uuid *uuid;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001160 int err;
1161
Szymon Janc4e51eae2011-02-25 19:05:48 +01001162 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001163
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001164 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001165 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1166 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001167
Szymon Janc4e51eae2011-02-25 19:05:48 +01001168 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001169 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001170 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1171 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001172
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001173 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001174
1175 uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
1176 if (!uuid) {
1177 err = -ENOMEM;
1178 goto failed;
1179 }
1180
1181 memcpy(uuid->uuid, cp->uuid, 16);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001182 uuid->svc_hint = cp->svc_hint;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001183
1184 list_add(&uuid->list, &hdev->uuids);
1185
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001186 err = update_class(hdev);
1187 if (err < 0)
1188 goto failed;
1189
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001190 err = update_eir(hdev);
1191 if (err < 0)
1192 goto failed;
1193
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001194 err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, 0, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001195
1196failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001197 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001198 hci_dev_put(hdev);
1199
1200 return err;
1201}
1202
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001203static int remove_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001204{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001205 struct mgmt_cp_remove_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001206 struct list_head *p, *n;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001207 struct hci_dev *hdev;
1208 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 +02001209 int err, found;
1210
Szymon Janc4e51eae2011-02-25 19:05:48 +01001211 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001212
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001213 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001214 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1215 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001216
Szymon Janc4e51eae2011-02-25 19:05:48 +01001217 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001218 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001219 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1220 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001221
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001222 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001223
1224 if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
1225 err = hci_uuids_clear(hdev);
1226 goto unlock;
1227 }
1228
1229 found = 0;
1230
1231 list_for_each_safe(p, n, &hdev->uuids) {
1232 struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
1233
1234 if (memcmp(match->uuid, cp->uuid, 16) != 0)
1235 continue;
1236
1237 list_del(&match->list);
1238 found++;
1239 }
1240
1241 if (found == 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001242 err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1243 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001244 goto unlock;
1245 }
1246
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001247 err = update_class(hdev);
1248 if (err < 0)
1249 goto unlock;
1250
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001251 err = update_eir(hdev);
1252 if (err < 0)
1253 goto unlock;
1254
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001255 err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, 0, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001256
1257unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001258 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001259 hci_dev_put(hdev);
1260
1261 return err;
1262}
1263
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001264static int set_dev_class(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001265{
1266 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001267 struct mgmt_cp_set_dev_class *cp = data;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001268 int err;
1269
Szymon Janc4e51eae2011-02-25 19:05:48 +01001270 BT_DBG("request for hci%u", index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001271
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001272 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001273 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1274 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001275
Szymon Janc4e51eae2011-02-25 19:05:48 +01001276 hdev = hci_dev_get(index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001277 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001278 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1279 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001280
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001281 hci_dev_lock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001282
Johan Hedbergb5235a62012-02-21 14:32:24 +02001283 if (!hdev_is_powered(hdev)) {
1284 err = cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1285 MGMT_STATUS_NOT_POWERED);
1286 goto unlock;
1287 }
1288
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001289 hdev->major_class = cp->major;
1290 hdev->minor_class = cp->minor;
1291
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001292 if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
Johan Hedberg7d785252011-12-15 00:47:39 +02001293 hci_dev_unlock(hdev);
1294 cancel_delayed_work_sync(&hdev->service_cache);
1295 hci_dev_lock(hdev);
Johan Hedberg14c0b602011-12-15 00:47:37 +02001296 update_eir(hdev);
Johan Hedberg7d785252011-12-15 00:47:39 +02001297 }
Johan Hedberg14c0b602011-12-15 00:47:37 +02001298
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001299 err = update_class(hdev);
1300
1301 if (err == 0)
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001302 err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, 0,
1303 NULL, 0);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001304
Johan Hedbergb5235a62012-02-21 14:32:24 +02001305unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001306 hci_dev_unlock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001307 hci_dev_put(hdev);
1308
1309 return err;
1310}
1311
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001312static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001313{
1314 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001315 struct mgmt_cp_load_link_keys *cp = data;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001316 u16 key_count, expected_len;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001317 int i;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001318
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001319 if (len < sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001320 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1321 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001322
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001323 key_count = get_unaligned_le16(&cp->key_count);
1324
Johan Hedberg86742e12011-11-07 23:13:38 +02001325 expected_len = sizeof(*cp) + key_count *
1326 sizeof(struct mgmt_link_key_info);
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001327 if (expected_len != len) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001328 BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001329 len, expected_len);
Johan Hedbergca69b792011-11-11 18:10:00 +02001330 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1331 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001332 }
1333
Szymon Janc4e51eae2011-02-25 19:05:48 +01001334 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001335 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001336 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1337 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001338
Szymon Janc4e51eae2011-02-25 19:05:48 +01001339 BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001340 key_count);
1341
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001342 hci_dev_lock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001343
1344 hci_link_keys_clear(hdev);
1345
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001346 set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001347
1348 if (cp->debug_keys)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001349 set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001350 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001351 clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001352
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001353 for (i = 0; i < key_count; i++) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001354 struct mgmt_link_key_info *key = &cp->keys[i];
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001355
Johan Hedbergd753fdc2012-02-17 14:06:34 +02001356 hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
1357 key->type, key->pin_len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001358 }
1359
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001360 cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
Johan Hedberg0e5f8752011-11-11 16:18:54 +02001361
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001362 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001363 hci_dev_put(hdev);
1364
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001365 return 0;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001366}
1367
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001368static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
1369 u8 addr_type, struct sock *skip_sk)
1370{
1371 struct mgmt_ev_device_unpaired ev;
1372
1373 bacpy(&ev.addr.bdaddr, bdaddr);
1374 ev.addr.type = addr_type;
1375
1376 return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev),
1377 skip_sk);
1378}
1379
Johan Hedberg124f6e32012-02-09 13:50:12 +02001380static int unpair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001381{
1382 struct hci_dev *hdev;
Johan Hedberg124f6e32012-02-09 13:50:12 +02001383 struct mgmt_cp_unpair_device *cp = data;
1384 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001385 struct hci_cp_disconnect dc;
1386 struct pending_cmd *cmd;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001387 struct hci_conn *conn;
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001388 u8 status = 0;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001389 int err;
1390
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001391 if (len != sizeof(*cp))
Johan Hedberg124f6e32012-02-09 13:50:12 +02001392 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001393 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001394
Szymon Janc4e51eae2011-02-25 19:05:48 +01001395 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001396 if (!hdev)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001397 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001398 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001399
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001400 hci_dev_lock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001401
Johan Hedberga8a1d192011-11-10 15:54:38 +02001402 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02001403 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1404 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001405
Johan Hedberg124f6e32012-02-09 13:50:12 +02001406 if (cp->addr.type == MGMT_ADDR_BREDR)
1407 err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
1408 else
1409 err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
Vinicius Costa Gomesb0dbfb42012-02-02 21:08:03 -03001410
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001411 if (err < 0) {
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001412 status = MGMT_STATUS_NOT_PAIRED;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001413 goto unlock;
1414 }
1415
Johan Hedberga8a1d192011-11-10 15:54:38 +02001416 if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001417 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1418 &rp, sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001419 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001420 goto unlock;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001421 }
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001422
Johan Hedberg124f6e32012-02-09 13:50:12 +02001423 if (cp->addr.type == MGMT_ADDR_BREDR)
1424 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
1425 &cp->addr.bdaddr);
1426 else
1427 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
1428 &cp->addr.bdaddr);
1429
Johan Hedberga8a1d192011-11-10 15:54:38 +02001430 if (!conn) {
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001431 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1432 &rp, sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001433 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberga8a1d192011-11-10 15:54:38 +02001434 goto unlock;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001435 }
1436
Johan Hedberg124f6e32012-02-09 13:50:12 +02001437 cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
1438 sizeof(*cp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02001439 if (!cmd) {
1440 err = -ENOMEM;
1441 goto unlock;
1442 }
1443
1444 put_unaligned_le16(conn->handle, &dc.handle);
1445 dc.reason = 0x13; /* Remote User Terminated Connection */
1446 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1447 if (err < 0)
1448 mgmt_pending_remove(cmd);
1449
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001450unlock:
Johan Hedbergca69b792011-11-11 18:10:00 +02001451 if (err < 0)
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001452 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, status,
1453 &rp, sizeof(rp));
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001454 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001455 hci_dev_put(hdev);
1456
1457 return err;
1458}
1459
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001460static int disconnect(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg8962ee72011-01-20 12:40:27 +02001461{
1462 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001463 struct mgmt_cp_disconnect *cp = data;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001464 struct hci_cp_disconnect dc;
Johan Hedberg366a0332011-02-19 12:05:55 -03001465 struct pending_cmd *cmd;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001466 struct hci_conn *conn;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001467 int err;
1468
1469 BT_DBG("");
1470
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001471 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001472 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1473 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001474
Szymon Janc4e51eae2011-02-25 19:05:48 +01001475 hdev = hci_dev_get(index);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001476 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001477 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1478 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001479
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001480 hci_dev_lock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001481
1482 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001483 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1484 MGMT_STATUS_NOT_POWERED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001485 goto failed;
1486 }
1487
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001488 if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001489 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1490 MGMT_STATUS_BUSY);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001491 goto failed;
1492 }
1493
Johan Hedberg88c3df12012-02-09 14:27:38 +02001494 if (cp->addr.type == MGMT_ADDR_BREDR)
1495 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
1496 else
1497 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
Vinicius Costa Gomes365227e2011-05-06 18:41:44 -03001498
Johan Hedberg8962ee72011-01-20 12:40:27 +02001499 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001500 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1501 MGMT_STATUS_NOT_CONNECTED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001502 goto failed;
1503 }
1504
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001505 cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001506 if (!cmd) {
1507 err = -ENOMEM;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001508 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001509 }
Johan Hedberg8962ee72011-01-20 12:40:27 +02001510
1511 put_unaligned_le16(conn->handle, &dc.handle);
1512 dc.reason = 0x13; /* Remote User Terminated Connection */
1513
1514 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1515 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001516 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001517
1518failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001519 hci_dev_unlock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001520 hci_dev_put(hdev);
1521
1522 return err;
1523}
1524
Johan Hedberg48264f02011-11-09 13:58:58 +02001525static u8 link_to_mgmt(u8 link_type, u8 addr_type)
Johan Hedberg4c659c32011-11-07 23:13:39 +02001526{
1527 switch (link_type) {
1528 case LE_LINK:
Johan Hedberg48264f02011-11-09 13:58:58 +02001529 switch (addr_type) {
1530 case ADDR_LE_DEV_PUBLIC:
1531 return MGMT_ADDR_LE_PUBLIC;
1532 case ADDR_LE_DEV_RANDOM:
1533 return MGMT_ADDR_LE_RANDOM;
1534 default:
1535 return MGMT_ADDR_INVALID;
1536 }
Johan Hedberg4c659c32011-11-07 23:13:39 +02001537 case ACL_LINK:
1538 return MGMT_ADDR_BREDR;
1539 default:
1540 return MGMT_ADDR_INVALID;
1541 }
1542}
1543
Szymon Janc8ce62842011-03-01 16:55:32 +01001544static int get_connections(struct sock *sk, u16 index)
Johan Hedberg2784eb42011-01-21 13:56:35 +02001545{
Johan Hedberg2784eb42011-01-21 13:56:35 +02001546 struct mgmt_rp_get_connections *rp;
1547 struct hci_dev *hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001548 struct hci_conn *c;
Johan Hedberga38528f2011-01-22 06:46:43 +02001549 size_t rp_len;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001550 u16 count;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001551 int i, err;
1552
1553 BT_DBG("");
1554
Szymon Janc4e51eae2011-02-25 19:05:48 +01001555 hdev = hci_dev_get(index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001556 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001557 return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
1558 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001559
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001560 hci_dev_lock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001561
1562 count = 0;
Johan Hedbergb644ba32012-01-17 21:48:47 +02001563 list_for_each_entry(c, &hdev->conn_hash.list, list) {
1564 if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1565 count++;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001566 }
1567
Johan Hedberg4c659c32011-11-07 23:13:39 +02001568 rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
Johan Hedberga38528f2011-01-22 06:46:43 +02001569 rp = kmalloc(rp_len, GFP_ATOMIC);
1570 if (!rp) {
Johan Hedberg2784eb42011-01-21 13:56:35 +02001571 err = -ENOMEM;
1572 goto unlock;
1573 }
1574
Johan Hedberg2784eb42011-01-21 13:56:35 +02001575 put_unaligned_le16(count, &rp->conn_count);
1576
Johan Hedberg2784eb42011-01-21 13:56:35 +02001577 i = 0;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001578 list_for_each_entry(c, &hdev->conn_hash.list, list) {
Johan Hedbergb644ba32012-01-17 21:48:47 +02001579 if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1580 continue;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001581 bacpy(&rp->addr[i].bdaddr, &c->dst);
Johan Hedberg48264f02011-11-09 13:58:58 +02001582 rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
Johan Hedberg4c659c32011-11-07 23:13:39 +02001583 if (rp->addr[i].type == MGMT_ADDR_INVALID)
1584 continue;
1585 i++;
1586 }
1587
1588 /* Recalculate length in case of filtered SCO connections, etc */
1589 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberg2784eb42011-01-21 13:56:35 +02001590
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001591 err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, 0, rp, rp_len);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001592
1593unlock:
Johan Hedberga38528f2011-01-22 06:46:43 +02001594 kfree(rp);
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001595 hci_dev_unlock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001596 hci_dev_put(hdev);
1597 return err;
1598}
1599
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001600static int send_pin_code_neg_reply(struct sock *sk, u16 index,
1601 struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
1602{
1603 struct pending_cmd *cmd;
1604 int err;
1605
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001606 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001607 sizeof(*cp));
1608 if (!cmd)
1609 return -ENOMEM;
1610
Johan Hedbergd8457692012-02-17 14:24:57 +02001611 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
1612 sizeof(cp->addr.bdaddr), &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001613 if (err < 0)
1614 mgmt_pending_remove(cmd);
1615
1616 return err;
1617}
1618
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001619static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001620{
1621 struct hci_dev *hdev;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001622 struct hci_conn *conn;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001623 struct mgmt_cp_pin_code_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001624 struct hci_cp_pin_code_reply reply;
Johan Hedberg366a0332011-02-19 12:05:55 -03001625 struct pending_cmd *cmd;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001626 int err;
1627
1628 BT_DBG("");
1629
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001630 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001631 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1632 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001633
Szymon Janc4e51eae2011-02-25 19:05:48 +01001634 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001635 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001636 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1637 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001638
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001639 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001640
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001641 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001642 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1643 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001644 goto failed;
1645 }
1646
Johan Hedbergd8457692012-02-17 14:24:57 +02001647 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001648 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001649 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1650 MGMT_STATUS_NOT_CONNECTED);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001651 goto failed;
1652 }
1653
1654 if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
Johan Hedbergd8457692012-02-17 14:24:57 +02001655 struct mgmt_cp_pin_code_neg_reply ncp;
1656
1657 memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr));
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001658
1659 BT_ERR("PIN code is not 16 bytes long");
1660
1661 err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
1662 if (err >= 0)
1663 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001664 MGMT_STATUS_INVALID_PARAMS);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001665
1666 goto failed;
1667 }
1668
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001669 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data,
1670 len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001671 if (!cmd) {
1672 err = -ENOMEM;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001673 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001674 }
Johan Hedberg980e1a52011-01-22 06:10:07 +02001675
Johan Hedbergd8457692012-02-17 14:24:57 +02001676 bacpy(&reply.bdaddr, &cp->addr.bdaddr);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001677 reply.pin_len = cp->pin_len;
Waldemar Rymarkiewicz24718ca2011-06-01 17:28:47 +02001678 memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
Johan Hedberg980e1a52011-01-22 06:10:07 +02001679
1680 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
1681 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001682 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001683
1684failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001685 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001686 hci_dev_put(hdev);
1687
1688 return err;
1689}
1690
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001691static int pin_code_neg_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001692{
1693 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001694 struct mgmt_cp_pin_code_neg_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001695 int err;
1696
1697 BT_DBG("");
1698
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001699 if (len != sizeof(*cp))
1700 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001701 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001702
Szymon Janc4e51eae2011-02-25 19:05:48 +01001703 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001704 if (!hdev)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001705 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001706 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001707
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001708 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001709
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001710 if (!hdev_is_powered(hdev)) {
Szymon Janc4e51eae2011-02-25 19:05:48 +01001711 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001712 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001713 goto failed;
1714 }
1715
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001716 err = send_pin_code_neg_reply(sk, index, hdev, cp);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001717
1718failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001719 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001720 hci_dev_put(hdev);
1721
1722 return err;
1723}
1724
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001725static int set_io_capability(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001726{
1727 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001728 struct mgmt_cp_set_io_capability *cp = data;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001729
1730 BT_DBG("");
1731
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001732 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001733 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1734 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001735
Szymon Janc4e51eae2011-02-25 19:05:48 +01001736 hdev = hci_dev_get(index);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001737 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001738 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1739 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001740
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001741 hci_dev_lock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001742
1743 hdev->io_capability = cp->io_capability;
1744
1745 BT_DBG("%s IO capability set to 0x%02x", hdev->name,
Szymon Jancb8534e02011-03-01 16:55:34 +01001746 hdev->io_capability);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001747
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001748 hci_dev_unlock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001749 hci_dev_put(hdev);
1750
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001751 return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, 0, NULL, 0);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001752}
1753
Johan Hedberge9a416b2011-02-19 12:05:56 -03001754static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1755{
1756 struct hci_dev *hdev = conn->hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001757 struct pending_cmd *cmd;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001758
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001759 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
Johan Hedberge9a416b2011-02-19 12:05:56 -03001760 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1761 continue;
1762
Johan Hedberge9a416b2011-02-19 12:05:56 -03001763 if (cmd->user_data != conn)
1764 continue;
1765
1766 return cmd;
1767 }
1768
1769 return NULL;
1770}
1771
1772static void pairing_complete(struct pending_cmd *cmd, u8 status)
1773{
1774 struct mgmt_rp_pair_device rp;
1775 struct hci_conn *conn = cmd->user_data;
1776
Johan Hedbergba4e5642011-11-11 00:07:34 +02001777 bacpy(&rp.addr.bdaddr, &conn->dst);
1778 rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001779
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001780 cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
1781 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001782
1783 /* So we don't get further callbacks for this connection */
1784 conn->connect_cfm_cb = NULL;
1785 conn->security_cfm_cb = NULL;
1786 conn->disconn_cfm_cb = NULL;
1787
1788 hci_conn_put(conn);
1789
Johan Hedberga664b5b2011-02-19 12:06:02 -03001790 mgmt_pending_remove(cmd);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001791}
1792
1793static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1794{
1795 struct pending_cmd *cmd;
1796
1797 BT_DBG("status %u", status);
1798
Johan Hedberg56e5cb82011-11-08 20:40:16 +02001799 cmd = find_pairing(conn);
1800 if (!cmd)
1801 BT_DBG("Unable to find a pending command");
1802 else
Johan Hedberge2113262012-02-18 15:20:03 +02001803 pairing_complete(cmd, mgmt_status(status));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001804}
1805
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001806static int pair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001807{
1808 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001809 struct mgmt_cp_pair_device *cp = data;
Johan Hedberg1425acb2011-11-11 00:07:35 +02001810 struct mgmt_rp_pair_device rp;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001811 struct pending_cmd *cmd;
1812 u8 sec_level, auth_type;
1813 struct hci_conn *conn;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001814 int err;
1815
1816 BT_DBG("");
1817
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001818 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001819 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1820 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001821
Szymon Janc4e51eae2011-02-25 19:05:48 +01001822 hdev = hci_dev_get(index);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001823 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001824 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1825 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001826
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001827 hci_dev_lock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001828
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001829 sec_level = BT_SECURITY_MEDIUM;
1830 if (cp->io_cap == 0x03)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001831 auth_type = HCI_AT_DEDICATED_BONDING;
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001832 else
Johan Hedberge9a416b2011-02-19 12:05:56 -03001833 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001834
Johan Hedbergba4e5642011-11-11 00:07:34 +02001835 if (cp->addr.type == MGMT_ADDR_BREDR)
1836 conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001837 auth_type);
1838 else
Johan Hedbergba4e5642011-11-11 00:07:34 +02001839 conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001840 auth_type);
1841
Johan Hedberg1425acb2011-11-11 00:07:35 +02001842 memset(&rp, 0, sizeof(rp));
1843 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1844 rp.addr.type = cp->addr.type;
1845
Ville Tervo30e76272011-02-22 16:10:53 -03001846 if (IS_ERR(conn)) {
Johan Hedberge2113262012-02-18 15:20:03 +02001847 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1848 MGMT_STATUS_CONNECT_FAILED,
1849 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001850 goto unlock;
1851 }
1852
1853 if (conn->connect_cfm_cb) {
1854 hci_conn_put(conn);
Johan Hedberge2113262012-02-18 15:20:03 +02001855 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1856 MGMT_STATUS_BUSY, &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001857 goto unlock;
1858 }
1859
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001860 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001861 if (!cmd) {
1862 err = -ENOMEM;
1863 hci_conn_put(conn);
1864 goto unlock;
1865 }
1866
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001867 /* For LE, just connecting isn't a proof that the pairing finished */
Johan Hedbergba4e5642011-11-11 00:07:34 +02001868 if (cp->addr.type == MGMT_ADDR_BREDR)
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001869 conn->connect_cfm_cb = pairing_complete_cb;
1870
Johan Hedberge9a416b2011-02-19 12:05:56 -03001871 conn->security_cfm_cb = pairing_complete_cb;
1872 conn->disconn_cfm_cb = pairing_complete_cb;
1873 conn->io_capability = cp->io_cap;
1874 cmd->user_data = conn;
1875
1876 if (conn->state == BT_CONNECTED &&
1877 hci_conn_security(conn, sec_level, auth_type))
1878 pairing_complete(cmd, 0);
1879
1880 err = 0;
1881
1882unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001883 hci_dev_unlock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001884 hci_dev_put(hdev);
1885
1886 return err;
1887}
1888
Johan Hedberg28424702012-02-02 04:02:29 +02001889static int cancel_pair_device(struct sock *sk, u16 index,
1890 unsigned char *data, u16 len)
1891{
1892 struct mgmt_addr_info *addr = (void *) data;
1893 struct hci_dev *hdev;
1894 struct pending_cmd *cmd;
1895 struct hci_conn *conn;
1896 int err;
1897
1898 BT_DBG("");
1899
1900 if (len != sizeof(*addr))
1901 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1902 MGMT_STATUS_INVALID_PARAMS);
1903
1904 hdev = hci_dev_get(index);
1905 if (!hdev)
1906 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1907 MGMT_STATUS_INVALID_PARAMS);
1908
1909 hci_dev_lock(hdev);
1910
1911 cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
1912 if (!cmd) {
1913 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1914 MGMT_STATUS_INVALID_PARAMS);
1915 goto unlock;
1916 }
1917
1918 conn = cmd->user_data;
1919
1920 if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
1921 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1922 MGMT_STATUS_INVALID_PARAMS);
1923 goto unlock;
1924 }
1925
1926 pairing_complete(cmd, MGMT_STATUS_CANCELLED);
1927
Johan Hedbergaee9b2182012-02-18 15:07:59 +02001928 err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, 0, addr,
Johan Hedberg28424702012-02-02 04:02:29 +02001929 sizeof(*addr));
1930unlock:
1931 hci_dev_unlock(hdev);
1932 hci_dev_put(hdev);
1933
1934 return err;
1935}
1936
Brian Gix0df4c182011-11-16 13:53:13 -08001937static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02001938 u8 type, u16 mgmt_op, u16 hci_op,
1939 __le32 passkey)
Johan Hedberga5c29682011-02-19 12:05:57 -03001940{
Johan Hedberga5c29682011-02-19 12:05:57 -03001941 struct pending_cmd *cmd;
1942 struct hci_dev *hdev;
Brian Gix0df4c182011-11-16 13:53:13 -08001943 struct hci_conn *conn;
Johan Hedberga5c29682011-02-19 12:05:57 -03001944 int err;
1945
Szymon Janc4e51eae2011-02-25 19:05:48 +01001946 hdev = hci_dev_get(index);
Johan Hedberga5c29682011-02-19 12:05:57 -03001947 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001948 return cmd_status(sk, index, mgmt_op,
1949 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberga5c29682011-02-19 12:05:57 -03001950
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001951 hci_dev_lock(hdev);
Johan Hedberg08ba5382011-03-16 14:29:34 +02001952
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001953 if (!hdev_is_powered(hdev)) {
Brian Gix0df4c182011-11-16 13:53:13 -08001954 err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
1955 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001956 }
1957
Johan Hedberg272d90d2012-02-09 15:26:12 +02001958 if (type == MGMT_ADDR_BREDR)
1959 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
1960 else
Brian Gix47c15e22011-11-16 13:53:14 -08001961 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
Brian Gix47c15e22011-11-16 13:53:14 -08001962
Johan Hedberg272d90d2012-02-09 15:26:12 +02001963 if (!conn) {
1964 err = cmd_status(sk, index, mgmt_op,
1965 MGMT_STATUS_NOT_CONNECTED);
1966 goto done;
1967 }
1968
1969 if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
Brian Gix47c15e22011-11-16 13:53:14 -08001970 /* Continue with pairing via SMP */
Brian Gix5fe57d92011-12-21 16:12:13 -08001971 err = smp_user_confirm_reply(conn, mgmt_op, passkey);
Brian Gix47c15e22011-11-16 13:53:14 -08001972
Brian Gix5fe57d92011-12-21 16:12:13 -08001973 if (!err)
1974 err = cmd_status(sk, index, mgmt_op,
1975 MGMT_STATUS_SUCCESS);
1976 else
1977 err = cmd_status(sk, index, mgmt_op,
1978 MGMT_STATUS_FAILED);
1979
Brian Gix47c15e22011-11-16 13:53:14 -08001980 goto done;
1981 }
1982
Brian Gix0df4c182011-11-16 13:53:13 -08001983 cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
Johan Hedberga5c29682011-02-19 12:05:57 -03001984 if (!cmd) {
1985 err = -ENOMEM;
Brian Gix0df4c182011-11-16 13:53:13 -08001986 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001987 }
1988
Brian Gix0df4c182011-11-16 13:53:13 -08001989 /* Continue with pairing via HCI */
Brian Gix604086b2011-11-23 08:28:33 -08001990 if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
1991 struct hci_cp_user_passkey_reply cp;
1992
1993 bacpy(&cp.bdaddr, bdaddr);
1994 cp.passkey = passkey;
1995 err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
1996 } else
1997 err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
1998
Johan Hedberga664b5b2011-02-19 12:06:02 -03001999 if (err < 0)
2000 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03002001
Brian Gix0df4c182011-11-16 13:53:13 -08002002done:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002003 hci_dev_unlock(hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03002004 hci_dev_put(hdev);
2005
2006 return err;
2007}
2008
Brian Gix0df4c182011-11-16 13:53:13 -08002009static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
2010{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002011 struct mgmt_cp_user_confirm_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08002012
2013 BT_DBG("");
2014
2015 if (len != sizeof(*cp))
2016 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
2017 MGMT_STATUS_INVALID_PARAMS);
2018
Johan Hedberg272d90d2012-02-09 15:26:12 +02002019 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2020 MGMT_OP_USER_CONFIRM_REPLY,
2021 HCI_OP_USER_CONFIRM_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08002022}
2023
2024static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
2025 u16 len)
2026{
Johan Hedbergc9c26592011-12-15 00:47:41 +02002027 struct mgmt_cp_user_confirm_neg_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08002028
2029 BT_DBG("");
2030
2031 if (len != sizeof(*cp))
2032 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
2033 MGMT_STATUS_INVALID_PARAMS);
2034
Johan Hedberg272d90d2012-02-09 15:26:12 +02002035 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2036 MGMT_OP_USER_CONFIRM_NEG_REPLY,
2037 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08002038}
2039
Brian Gix604086b2011-11-23 08:28:33 -08002040static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
2041{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002042 struct mgmt_cp_user_passkey_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08002043
2044 BT_DBG("");
2045
2046 if (len != sizeof(*cp))
2047 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
2048 EINVAL);
2049
Johan Hedberg272d90d2012-02-09 15:26:12 +02002050 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2051 MGMT_OP_USER_PASSKEY_REPLY,
2052 HCI_OP_USER_PASSKEY_REPLY,
2053 cp->passkey);
Brian Gix604086b2011-11-23 08:28:33 -08002054}
2055
2056static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
2057 u16 len)
2058{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002059 struct mgmt_cp_user_passkey_neg_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08002060
2061 BT_DBG("");
2062
2063 if (len != sizeof(*cp))
2064 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
2065 EINVAL);
2066
Johan Hedberg272d90d2012-02-09 15:26:12 +02002067 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
2068 MGMT_OP_USER_PASSKEY_NEG_REPLY,
2069 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
Brian Gix604086b2011-11-23 08:28:33 -08002070}
2071
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002072static int set_local_name(struct sock *sk, u16 index, void *data,
Johan Hedbergb312b1612011-03-16 14:29:37 +02002073 u16 len)
2074{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002075 struct mgmt_cp_set_local_name *mgmt_cp = data;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002076 struct hci_cp_write_local_name hci_cp;
2077 struct hci_dev *hdev;
2078 struct pending_cmd *cmd;
2079 int err;
2080
2081 BT_DBG("");
2082
2083 if (len != sizeof(*mgmt_cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02002084 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2085 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002086
2087 hdev = hci_dev_get(index);
2088 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002089 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2090 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002091
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002092 hci_dev_lock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002093
Johan Hedbergb5235a62012-02-21 14:32:24 +02002094 if (!hdev_is_powered(hdev)) {
2095 err = cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2096 MGMT_STATUS_NOT_POWERED);
2097 goto failed;
2098 }
2099
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002100 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data,
2101 len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002102 if (!cmd) {
2103 err = -ENOMEM;
2104 goto failed;
2105 }
2106
2107 memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
2108 err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
2109 &hci_cp);
2110 if (err < 0)
2111 mgmt_pending_remove(cmd);
2112
2113failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002114 hci_dev_unlock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002115 hci_dev_put(hdev);
2116
2117 return err;
2118}
2119
Szymon Jancc35938b2011-03-22 13:12:21 +01002120static int read_local_oob_data(struct sock *sk, u16 index)
2121{
2122 struct hci_dev *hdev;
2123 struct pending_cmd *cmd;
2124 int err;
2125
2126 BT_DBG("hci%u", index);
2127
2128 hdev = hci_dev_get(index);
2129 if (!hdev)
2130 return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002131 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancc35938b2011-03-22 13:12:21 +01002132
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002133 hci_dev_lock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002134
Johan Hedberg4b34ee782012-02-21 14:13:02 +02002135 if (!hdev_is_powered(hdev)) {
Szymon Jancc35938b2011-03-22 13:12:21 +01002136 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002137 MGMT_STATUS_NOT_POWERED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002138 goto unlock;
2139 }
2140
2141 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
2142 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002143 MGMT_STATUS_NOT_SUPPORTED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002144 goto unlock;
2145 }
2146
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002147 if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002148 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
2149 MGMT_STATUS_BUSY);
Szymon Jancc35938b2011-03-22 13:12:21 +01002150 goto unlock;
2151 }
2152
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002153 cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
Szymon Jancc35938b2011-03-22 13:12:21 +01002154 if (!cmd) {
2155 err = -ENOMEM;
2156 goto unlock;
2157 }
2158
2159 err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
2160 if (err < 0)
2161 mgmt_pending_remove(cmd);
2162
2163unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002164 hci_dev_unlock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002165 hci_dev_put(hdev);
2166
2167 return err;
2168}
2169
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002170static int add_remote_oob_data(struct sock *sk, u16 index, void *data,
2171 u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002172{
2173 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002174 struct mgmt_cp_add_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002175 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002176 int err;
2177
2178 BT_DBG("hci%u ", index);
2179
2180 if (len != sizeof(*cp))
2181 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002182 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002183
2184 hdev = hci_dev_get(index);
2185 if (!hdev)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002186 return cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
2187 MGMT_STATUS_INVALID_PARAMS,
2188 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002189
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002190 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002191
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002192 err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
Szymon Janc2763eda2011-03-22 13:12:22 +01002193 cp->randomizer);
2194 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002195 status = MGMT_STATUS_FAILED;
Szymon Janc2763eda2011-03-22 13:12:22 +01002196 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002197 status = 0;
2198
2199 err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
2200 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002201
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002202 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002203 hci_dev_put(hdev);
2204
2205 return err;
2206}
2207
2208static int remove_remote_oob_data(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002209 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002210{
2211 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002212 struct mgmt_cp_remove_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002213 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002214 int err;
2215
2216 BT_DBG("hci%u ", index);
2217
2218 if (len != sizeof(*cp))
2219 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002220 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002221
2222 hdev = hci_dev_get(index);
2223 if (!hdev)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002224 return cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
2225 MGMT_STATUS_INVALID_PARAMS,
2226 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002227
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002228 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002229
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002230 err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
Szymon Janc2763eda2011-03-22 13:12:22 +01002231 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002232 status = MGMT_STATUS_INVALID_PARAMS;
Szymon Janc2763eda2011-03-22 13:12:22 +01002233 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002234 status = 0;
2235
2236 err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA, status,
2237 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002238
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002239 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002240 hci_dev_put(hdev);
2241
2242 return err;
2243}
2244
Andre Guedes5e0452c2012-02-17 20:39:38 -03002245static int discovery(struct hci_dev *hdev)
2246{
2247 int err;
2248
2249 if (lmp_host_le_capable(hdev)) {
2250 if (lmp_bredr_capable(hdev)) {
2251 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2252 LE_SCAN_INT, LE_SCAN_WIN,
2253 LE_SCAN_TIMEOUT_BREDR_LE);
2254 } else {
2255 hdev->discovery.type = DISCOV_TYPE_LE;
2256 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2257 LE_SCAN_INT, LE_SCAN_WIN,
2258 LE_SCAN_TIMEOUT_LE_ONLY);
2259 }
2260 } else {
2261 hdev->discovery.type = DISCOV_TYPE_BREDR;
2262 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
2263 }
2264
2265 return err;
2266}
2267
2268int mgmt_interleaved_discovery(struct hci_dev *hdev)
2269{
2270 int err;
2271
2272 BT_DBG("%s", hdev->name);
2273
2274 hci_dev_lock(hdev);
2275
2276 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
2277 if (err < 0)
2278 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2279
2280 hci_dev_unlock(hdev);
2281
2282 return err;
2283}
2284
Johan Hedberg450dfda2011-11-12 11:58:22 +02002285static int start_discovery(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002286 void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002287{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002288 struct mgmt_cp_start_discovery *cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002289 struct pending_cmd *cmd;
2290 struct hci_dev *hdev;
2291 int err;
2292
2293 BT_DBG("hci%u", index);
2294
Johan Hedberg450dfda2011-11-12 11:58:22 +02002295 if (len != sizeof(*cp))
2296 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2297 MGMT_STATUS_INVALID_PARAMS);
2298
Johan Hedberg14a53662011-04-27 10:29:56 -04002299 hdev = hci_dev_get(index);
2300 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002301 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2302 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002303
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002304 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002305
Johan Hedberg4b34ee782012-02-21 14:13:02 +02002306 if (!hdev_is_powered(hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002307 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2308 MGMT_STATUS_NOT_POWERED);
Johan Hedbergbd2d1332011-11-07 23:13:37 +02002309 goto failed;
2310 }
2311
Johan Hedbergff9ef572012-01-04 14:23:45 +02002312 if (hdev->discovery.state != DISCOVERY_STOPPED) {
2313 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2314 MGMT_STATUS_BUSY);
2315 goto failed;
2316 }
2317
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002318 cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002319 if (!cmd) {
2320 err = -ENOMEM;
2321 goto failed;
2322 }
2323
Andre Guedes4aab14e2012-02-17 20:39:36 -03002324 hdev->discovery.type = cp->type;
2325
2326 switch (hdev->discovery.type) {
Andre Guedesf39799f2012-02-17 20:39:35 -03002327 case DISCOV_TYPE_BREDR:
Andre Guedes3fd24152012-02-03 17:48:01 -03002328 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
Andre Guedesf39799f2012-02-17 20:39:35 -03002329 break;
2330
2331 case DISCOV_TYPE_LE:
Andre Guedes3fd24152012-02-03 17:48:01 -03002332 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
2333 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
Andre Guedesf39799f2012-02-17 20:39:35 -03002334 break;
2335
Andre Guedes5e0452c2012-02-17 20:39:38 -03002336 case DISCOV_TYPE_INTERLEAVED:
2337 err = discovery(hdev);
2338 break;
2339
Andre Guedesf39799f2012-02-17 20:39:35 -03002340 default:
Andre Guedes3fd24152012-02-03 17:48:01 -03002341 err = -EINVAL;
Andre Guedesf39799f2012-02-17 20:39:35 -03002342 }
Andre Guedes3fd24152012-02-03 17:48:01 -03002343
Johan Hedberg14a53662011-04-27 10:29:56 -04002344 if (err < 0)
2345 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002346 else
2347 hci_discovery_set_state(hdev, DISCOVERY_STARTING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002348
2349failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002350 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002351 hci_dev_put(hdev);
2352
2353 return err;
2354}
2355
Johan Hedbergd9306502012-02-20 23:25:18 +02002356static int stop_discovery(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002357{
Johan Hedbergd9306502012-02-20 23:25:18 +02002358 struct mgmt_cp_stop_discovery *mgmt_cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002359 struct hci_dev *hdev;
2360 struct pending_cmd *cmd;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002361 struct hci_cp_remote_name_req_cancel cp;
2362 struct inquiry_entry *e;
Johan Hedberg14a53662011-04-27 10:29:56 -04002363 int err;
2364
2365 BT_DBG("hci%u", index);
2366
Johan Hedbergd9306502012-02-20 23:25:18 +02002367 if (len != sizeof(*mgmt_cp))
2368 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2369 MGMT_STATUS_INVALID_PARAMS);
2370
Johan Hedberg14a53662011-04-27 10:29:56 -04002371 hdev = hci_dev_get(index);
2372 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002373 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2374 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002375
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002376 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002377
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002378 if (!hci_discovery_active(hdev)) {
Johan Hedbergd9306502012-02-20 23:25:18 +02002379 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY,
2380 MGMT_STATUS_REJECTED,
2381 &mgmt_cp->type, sizeof(mgmt_cp->type));
2382 goto unlock;
2383 }
2384
2385 if (hdev->discovery.type != mgmt_cp->type) {
2386 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY,
2387 MGMT_STATUS_INVALID_PARAMS,
2388 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002389 goto unlock;
Johan Hedbergff9ef572012-01-04 14:23:45 +02002390 }
2391
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002392 cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002393 if (!cmd) {
2394 err = -ENOMEM;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002395 goto unlock;
Johan Hedberg14a53662011-04-27 10:29:56 -04002396 }
2397
Andre Guedes343f9352012-02-17 20:39:37 -03002398 if (hdev->discovery.state == DISCOVERY_FINDING) {
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002399 err = hci_cancel_inquiry(hdev);
2400 if (err < 0)
2401 mgmt_pending_remove(cmd);
2402 else
2403 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
2404 goto unlock;
2405 }
2406
2407 e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
2408 if (!e) {
2409 mgmt_pending_remove(cmd);
Johan Hedbergaee9b2182012-02-18 15:07:59 +02002410 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, 0,
Johan Hedbergd9306502012-02-20 23:25:18 +02002411 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002412 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2413 goto unlock;
2414 }
2415
2416 bacpy(&cp.bdaddr, &e->data.bdaddr);
2417 err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
2418 sizeof(cp), &cp);
Johan Hedberg14a53662011-04-27 10:29:56 -04002419 if (err < 0)
2420 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002421 else
2422 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002423
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002424unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002425 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002426 hci_dev_put(hdev);
2427
2428 return err;
2429}
2430
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002431static int confirm_name(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg561aafb2012-01-04 13:31:59 +02002432{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002433 struct mgmt_cp_confirm_name *cp = data;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002434 struct inquiry_entry *e;
2435 struct hci_dev *hdev;
2436 int err;
2437
2438 BT_DBG("hci%u", index);
2439
2440 if (len != sizeof(*cp))
2441 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2442 MGMT_STATUS_INVALID_PARAMS);
2443
2444 hdev = hci_dev_get(index);
2445 if (!hdev)
2446 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2447 MGMT_STATUS_INVALID_PARAMS);
2448
2449 hci_dev_lock(hdev);
2450
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002451 if (!hci_discovery_active(hdev)) {
2452 err = cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2453 MGMT_STATUS_FAILED);
2454 goto failed;
2455 }
2456
Johan Hedberga198e7b2012-02-17 14:27:06 +02002457 e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002458 if (!e) {
2459 err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME,
2460 MGMT_STATUS_INVALID_PARAMS);
2461 goto failed;
2462 }
2463
2464 if (cp->name_known) {
2465 e->name_state = NAME_KNOWN;
2466 list_del(&e->list);
2467 } else {
2468 e->name_state = NAME_NEEDED;
Johan Hedberga3d4e202012-01-09 00:53:02 +02002469 hci_inquiry_cache_update_resolve(hdev, e);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002470 }
2471
2472 err = 0;
2473
2474failed:
2475 hci_dev_unlock(hdev);
2476
2477 return err;
2478}
2479
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002480static int block_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002481{
2482 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002483 struct mgmt_cp_block_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002484 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002485 int err;
2486
2487 BT_DBG("hci%u", index);
2488
Antti Julku7fbec222011-06-15 12:01:15 +03002489 if (len != sizeof(*cp))
2490 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002491 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002492
2493 hdev = hci_dev_get(index);
2494 if (!hdev)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002495 return cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
2496 MGMT_STATUS_INVALID_PARAMS,
2497 &cp->addr, sizeof(cp->addr));
Antti Julku7fbec222011-06-15 12:01:15 +03002498
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002499 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002500
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002501 err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002502 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002503 status = MGMT_STATUS_FAILED;
Antti Julku7fbec222011-06-15 12:01:15 +03002504 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002505 status = 0;
2506
2507 err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, status,
2508 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002509
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002510 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002511 hci_dev_put(hdev);
2512
2513 return err;
2514}
2515
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002516static int unblock_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002517{
2518 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002519 struct mgmt_cp_unblock_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002520 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002521 int err;
2522
2523 BT_DBG("hci%u", index);
2524
Antti Julku7fbec222011-06-15 12:01:15 +03002525 if (len != sizeof(*cp))
2526 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002527 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002528
2529 hdev = hci_dev_get(index);
2530 if (!hdev)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002531 return cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2532 MGMT_STATUS_INVALID_PARAMS,
2533 &cp->addr, sizeof(cp->addr));
Antti Julku7fbec222011-06-15 12:01:15 +03002534
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002535 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002536
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002537 err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002538 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002539 status = MGMT_STATUS_INVALID_PARAMS;
Antti Julku7fbec222011-06-15 12:01:15 +03002540 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002541 status = 0;
2542
2543 err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, status,
2544 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002545
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002546 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002547 hci_dev_put(hdev);
2548
2549 return err;
2550}
2551
Antti Julkuf6422ec2011-06-22 13:11:56 +03002552static int set_fast_connectable(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002553 void *data, u16 len)
Antti Julkuf6422ec2011-06-22 13:11:56 +03002554{
2555 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002556 struct mgmt_mode *cp = data;
Antti Julkuf6422ec2011-06-22 13:11:56 +03002557 struct hci_cp_write_page_scan_activity acp;
2558 u8 type;
2559 int err;
2560
2561 BT_DBG("hci%u", index);
2562
2563 if (len != sizeof(*cp))
2564 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002565 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002566
2567 hdev = hci_dev_get(index);
2568 if (!hdev)
2569 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002570 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg5400c042012-02-21 16:40:33 +02002571 if (!hdev_is_powered(hdev))
2572 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
2573 MGMT_STATUS_NOT_POWERED);
2574
2575 if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
2576 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
2577 MGMT_STATUS_REJECTED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002578
2579 hci_dev_lock(hdev);
2580
Johan Hedbergf7c68692011-12-15 00:47:36 +02002581 if (cp->val) {
Antti Julkuf6422ec2011-06-22 13:11:56 +03002582 type = PAGE_SCAN_TYPE_INTERLACED;
2583 acp.interval = 0x0024; /* 22.5 msec page scan interval */
2584 } else {
2585 type = PAGE_SCAN_TYPE_STANDARD; /* default */
2586 acp.interval = 0x0800; /* default 1.28 sec page scan */
2587 }
2588
2589 acp.window = 0x0012; /* default 11.25 msec page scan window */
2590
2591 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
2592 sizeof(acp), &acp);
2593 if (err < 0) {
2594 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002595 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002596 goto done;
2597 }
2598
2599 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
2600 if (err < 0) {
2601 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002602 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002603 goto done;
2604 }
2605
Johan Hedbergaee9b2182012-02-18 15:07:59 +02002606 err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, 0,
2607 NULL, 0);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002608done:
2609 hci_dev_unlock(hdev);
2610 hci_dev_put(hdev);
2611
2612 return err;
2613}
2614
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002615static int load_long_term_keys(struct sock *sk, u16 index,
2616 void *cp_data, u16 len)
2617{
2618 struct hci_dev *hdev;
2619 struct mgmt_cp_load_long_term_keys *cp = cp_data;
2620 u16 key_count, expected_len;
2621 int i;
2622
2623 if (len < sizeof(*cp))
2624 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2625 EINVAL);
2626
2627 key_count = get_unaligned_le16(&cp->key_count);
2628
2629 expected_len = sizeof(*cp) + key_count *
2630 sizeof(struct mgmt_ltk_info);
2631 if (expected_len != len) {
2632 BT_ERR("load_keys: expected %u bytes, got %u bytes",
2633 len, expected_len);
2634 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2635 EINVAL);
2636 }
2637
2638 hdev = hci_dev_get(index);
2639 if (!hdev)
2640 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2641 ENODEV);
2642
2643 BT_DBG("hci%u key_count %u", index, key_count);
2644
2645 hci_dev_lock(hdev);
2646
2647 hci_smp_ltks_clear(hdev);
2648
2649 for (i = 0; i < key_count; i++) {
2650 struct mgmt_ltk_info *key = &cp->keys[i];
2651 u8 type;
2652
2653 if (key->master)
2654 type = HCI_SMP_LTK;
2655 else
2656 type = HCI_SMP_LTK_SLAVE;
2657
2658 hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
2659 type, 0, key->authenticated, key->val,
2660 key->enc_size, key->ediv, key->rand);
2661 }
2662
2663 hci_dev_unlock(hdev);
2664 hci_dev_put(hdev);
2665
2666 return 0;
2667}
2668
Johan Hedberg03811012010-12-08 00:21:06 +02002669int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
2670{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002671 void *buf;
2672 u8 *cp;
Johan Hedberg03811012010-12-08 00:21:06 +02002673 struct mgmt_hdr *hdr;
Szymon Janc4e51eae2011-02-25 19:05:48 +01002674 u16 opcode, index, len;
Johan Hedberg03811012010-12-08 00:21:06 +02002675 int err;
2676
2677 BT_DBG("got %zu bytes", msglen);
2678
2679 if (msglen < sizeof(*hdr))
2680 return -EINVAL;
2681
Gustavo F. Padovane63a15e2011-04-04 18:56:53 -03002682 buf = kmalloc(msglen, GFP_KERNEL);
Johan Hedberg03811012010-12-08 00:21:06 +02002683 if (!buf)
2684 return -ENOMEM;
2685
2686 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
2687 err = -EFAULT;
2688 goto done;
2689 }
2690
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002691 hdr = buf;
Johan Hedberg03811012010-12-08 00:21:06 +02002692 opcode = get_unaligned_le16(&hdr->opcode);
Szymon Janc4e51eae2011-02-25 19:05:48 +01002693 index = get_unaligned_le16(&hdr->index);
Johan Hedberg03811012010-12-08 00:21:06 +02002694 len = get_unaligned_le16(&hdr->len);
2695
2696 if (len != msglen - sizeof(*hdr)) {
2697 err = -EINVAL;
2698 goto done;
2699 }
2700
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002701 cp = buf + sizeof(*hdr);
2702
Johan Hedberg03811012010-12-08 00:21:06 +02002703 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +02002704 case MGMT_OP_READ_VERSION:
2705 err = read_version(sk);
2706 break;
Johan Hedberge70bb2e2012-02-13 16:59:33 +02002707 case MGMT_OP_READ_COMMANDS:
2708 err = read_commands(sk);
2709 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +02002710 case MGMT_OP_READ_INDEX_LIST:
2711 err = read_index_list(sk);
2712 break;
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002713 case MGMT_OP_READ_INFO:
Szymon Janc4e51eae2011-02-25 19:05:48 +01002714 err = read_controller_info(sk, index);
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002715 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002716 case MGMT_OP_SET_POWERED:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002717 err = set_powered(sk, index, cp, len);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002718 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +02002719 case MGMT_OP_SET_DISCOVERABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002720 err = set_discoverable(sk, index, cp, len);
Johan Hedberg73f22f62010-12-29 16:00:25 +02002721 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002722 case MGMT_OP_SET_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002723 err = set_connectable(sk, index, cp, len);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002724 break;
Johan Hedbergf7c68692011-12-15 00:47:36 +02002725 case MGMT_OP_SET_FAST_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002726 err = set_fast_connectable(sk, index, cp, len);
Johan Hedbergf7c68692011-12-15 00:47:36 +02002727 break;
Johan Hedbergc542a062011-01-26 13:11:03 +02002728 case MGMT_OP_SET_PAIRABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002729 err = set_pairable(sk, index, cp, len);
Johan Hedbergc542a062011-01-26 13:11:03 +02002730 break;
Johan Hedberg33ef95e2012-02-16 23:56:27 +02002731 case MGMT_OP_SET_LINK_SECURITY:
2732 err = set_link_security(sk, index, cp, len);
2733 break;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02002734 case MGMT_OP_SET_SSP:
2735 err = set_ssp(sk, index, cp, len);
2736 break;
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02002737 case MGMT_OP_SET_HS:
2738 err = set_hs(sk, index, cp, len);
2739 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002740 case MGMT_OP_ADD_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002741 err = add_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002742 break;
2743 case MGMT_OP_REMOVE_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002744 err = remove_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002745 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002746 case MGMT_OP_SET_DEV_CLASS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002747 err = set_dev_class(sk, index, cp, len);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002748 break;
Johan Hedberg86742e12011-11-07 23:13:38 +02002749 case MGMT_OP_LOAD_LINK_KEYS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002750 err = load_link_keys(sk, index, cp, len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02002751 break;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002752 case MGMT_OP_DISCONNECT:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002753 err = disconnect(sk, index, cp, len);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002754 break;
Johan Hedberg2784eb42011-01-21 13:56:35 +02002755 case MGMT_OP_GET_CONNECTIONS:
Szymon Janc8ce62842011-03-01 16:55:32 +01002756 err = get_connections(sk, index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02002757 break;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002758 case MGMT_OP_PIN_CODE_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002759 err = pin_code_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002760 break;
2761 case MGMT_OP_PIN_CODE_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002762 err = pin_code_neg_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002763 break;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002764 case MGMT_OP_SET_IO_CAPABILITY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002765 err = set_io_capability(sk, index, cp, len);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002766 break;
Johan Hedberge9a416b2011-02-19 12:05:56 -03002767 case MGMT_OP_PAIR_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002768 err = pair_device(sk, index, cp, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002769 break;
Johan Hedberg28424702012-02-02 04:02:29 +02002770 case MGMT_OP_CANCEL_PAIR_DEVICE:
2771 err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len);
2772 break;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002773 case MGMT_OP_UNPAIR_DEVICE:
2774 err = unpair_device(sk, index, cp, len);
2775 break;
Johan Hedberga5c29682011-02-19 12:05:57 -03002776 case MGMT_OP_USER_CONFIRM_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002777 err = user_confirm_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002778 break;
2779 case MGMT_OP_USER_CONFIRM_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002780 err = user_confirm_neg_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002781 break;
Brian Gix604086b2011-11-23 08:28:33 -08002782 case MGMT_OP_USER_PASSKEY_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002783 err = user_passkey_reply(sk, index, cp, len);
Brian Gix604086b2011-11-23 08:28:33 -08002784 break;
2785 case MGMT_OP_USER_PASSKEY_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002786 err = user_passkey_neg_reply(sk, index, cp, len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02002787 break;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002788 case MGMT_OP_SET_LOCAL_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002789 err = set_local_name(sk, index, cp, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002790 break;
Szymon Jancc35938b2011-03-22 13:12:21 +01002791 case MGMT_OP_READ_LOCAL_OOB_DATA:
2792 err = read_local_oob_data(sk, index);
2793 break;
Szymon Janc2763eda2011-03-22 13:12:22 +01002794 case MGMT_OP_ADD_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002795 err = add_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002796 break;
2797 case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002798 err = remove_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002799 break;
Johan Hedberg14a53662011-04-27 10:29:56 -04002800 case MGMT_OP_START_DISCOVERY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002801 err = start_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002802 break;
2803 case MGMT_OP_STOP_DISCOVERY:
Johan Hedbergd9306502012-02-20 23:25:18 +02002804 err = stop_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002805 break;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002806 case MGMT_OP_CONFIRM_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002807 err = confirm_name(sk, index, cp, len);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002808 break;
Antti Julku7fbec222011-06-15 12:01:15 +03002809 case MGMT_OP_BLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002810 err = block_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002811 break;
2812 case MGMT_OP_UNBLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002813 err = unblock_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002814 break;
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002815 case MGMT_OP_LOAD_LONG_TERM_KEYS:
2816 err = load_long_term_keys(sk, index, cp, len);
2817 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002818 default:
Johan Hedberg72a734e2010-12-30 00:38:22 +02002819 BT_DBG("Unknown op %u", opcode);
Johan Hedbergca69b792011-11-11 18:10:00 +02002820 err = cmd_status(sk, index, opcode,
2821 MGMT_STATUS_UNKNOWN_COMMAND);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002822 break;
2823 }
Johan Hedberg72a734e2010-12-30 00:38:22 +02002824
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002825 if (err < 0)
Johan Hedberg72a734e2010-12-30 00:38:22 +02002826 goto done;
2827
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002828 err = msglen;
2829
2830done:
2831 kfree(buf);
2832 return err;
2833}
2834
Johan Hedbergb24752f2011-11-03 14:40:33 +02002835static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
2836{
2837 u8 *status = data;
2838
2839 cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
2840 mgmt_pending_remove(cmd);
2841}
2842
Johan Hedberg744cf192011-11-08 20:40:14 +02002843int mgmt_index_added(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002844{
Johan Hedberg744cf192011-11-08 20:40:14 +02002845 return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002846}
2847
Johan Hedberg744cf192011-11-08 20:40:14 +02002848int mgmt_index_removed(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002849{
Johan Hedbergb24752f2011-11-03 14:40:33 +02002850 u8 status = ENODEV;
2851
Johan Hedberg744cf192011-11-08 20:40:14 +02002852 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002853
Johan Hedberg744cf192011-11-08 20:40:14 +02002854 return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002855}
2856
2857struct cmd_lookup {
Johan Hedberg03811012010-12-08 00:21:06 +02002858 struct sock *sk;
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002859 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +02002860};
2861
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002862static void settings_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberg03811012010-12-08 00:21:06 +02002863{
Johan Hedberg03811012010-12-08 00:21:06 +02002864 struct cmd_lookup *match = data;
2865
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002866 send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
Johan Hedberg03811012010-12-08 00:21:06 +02002867
2868 list_del(&cmd->list);
2869
2870 if (match->sk == NULL) {
2871 match->sk = cmd->sk;
2872 sock_hold(match->sk);
2873 }
2874
2875 mgmt_pending_free(cmd);
2876}
2877
Johan Hedberg744cf192011-11-08 20:40:14 +02002878int mgmt_powered(struct hci_dev *hdev, u8 powered)
Johan Hedberg03811012010-12-08 00:21:06 +02002879{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002880 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002881 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002882 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002883
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002884 mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002885
Johan Hedbergb24752f2011-11-03 14:40:33 +02002886 if (!powered) {
2887 u8 status = ENETDOWN;
Johan Hedberg744cf192011-11-08 20:40:14 +02002888 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002889 }
2890
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002891 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002892
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002893 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002894 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002895
2896 if (match.sk)
2897 sock_put(match.sk);
2898
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002899 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002900}
2901
Johan Hedberg744cf192011-11-08 20:40:14 +02002902int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
Johan Hedberg03811012010-12-08 00:21:06 +02002903{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002904 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002905 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002906 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002907
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002908 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002909
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002910 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002911
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002912 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002913 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002914 if (match.sk)
2915 sock_put(match.sk);
2916
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002917 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002918}
2919
Johan Hedberg744cf192011-11-08 20:40:14 +02002920int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
Johan Hedberg03811012010-12-08 00:21:06 +02002921{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002922 __le32 ev;
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002923 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002924 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002925
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002926 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
2927 &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002928
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002929 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002930
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002931 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002932
2933 if (match.sk)
2934 sock_put(match.sk);
2935
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002936 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002937}
2938
Johan Hedberg744cf192011-11-08 20:40:14 +02002939int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002940{
Johan Hedbergca69b792011-11-11 18:10:00 +02002941 u8 mgmt_err = mgmt_status(status);
2942
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002943 if (scan & SCAN_PAGE)
Johan Hedberg744cf192011-11-08 20:40:14 +02002944 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002945 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002946
2947 if (scan & SCAN_INQUIRY)
Johan Hedberg744cf192011-11-08 20:40:14 +02002948 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002949 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002950
2951 return 0;
2952}
2953
Johan Hedberg744cf192011-11-08 20:40:14 +02002954int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
2955 u8 persistent)
Johan Hedberg03811012010-12-08 00:21:06 +02002956{
Johan Hedberg86742e12011-11-07 23:13:38 +02002957 struct mgmt_ev_new_link_key ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002958
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002959 memset(&ev, 0, sizeof(ev));
Johan Hedberg03811012010-12-08 00:21:06 +02002960
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002961 ev.store_hint = persistent;
Johan Hedbergd753fdc2012-02-17 14:06:34 +02002962 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2963 ev.key.addr.type = MGMT_ADDR_BREDR;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002964 ev.key.type = key->type;
2965 memcpy(ev.key.val, key->val, 16);
2966 ev.key.pin_len = key->pin_len;
Johan Hedberg03811012010-12-08 00:21:06 +02002967
Johan Hedberg744cf192011-11-08 20:40:14 +02002968 return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002969}
Johan Hedbergf7520542011-01-20 12:34:39 +02002970
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002971int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
2972{
2973 struct mgmt_ev_new_long_term_key ev;
2974
2975 memset(&ev, 0, sizeof(ev));
2976
2977 ev.store_hint = persistent;
2978 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2979 ev.key.addr.type = key->bdaddr_type;
2980 ev.key.authenticated = key->authenticated;
2981 ev.key.enc_size = key->enc_size;
2982 ev.key.ediv = key->ediv;
2983
2984 if (key->type == HCI_SMP_LTK)
2985 ev.key.master = 1;
2986
2987 memcpy(ev.key.rand, key->rand, sizeof(key->rand));
2988 memcpy(ev.key.val, key->val, sizeof(key->val));
2989
2990 return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
2991 &ev, sizeof(ev), NULL);
2992}
2993
Johan Hedbergafc747a2012-01-15 18:11:07 +02002994int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedbergb644ba32012-01-17 21:48:47 +02002995 u8 addr_type, u8 *name, u8 name_len,
2996 u8 *dev_class)
Johan Hedbergf7520542011-01-20 12:34:39 +02002997{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002998 char buf[512];
2999 struct mgmt_ev_device_connected *ev = (void *) buf;
3000 u16 eir_len = 0;
Johan Hedbergf7520542011-01-20 12:34:39 +02003001
Johan Hedbergb644ba32012-01-17 21:48:47 +02003002 bacpy(&ev->addr.bdaddr, bdaddr);
3003 ev->addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02003004
Johan Hedbergb644ba32012-01-17 21:48:47 +02003005 if (name_len > 0)
3006 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
3007 name, name_len);
3008
3009 if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
3010 eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
3011 EIR_CLASS_OF_DEV, dev_class, 3);
3012
3013 put_unaligned_le16(eir_len, &ev->eir_len);
3014
3015 return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
3016 sizeof(*ev) + eir_len, NULL);
Johan Hedbergf7520542011-01-20 12:34:39 +02003017}
3018
Johan Hedberg8962ee72011-01-20 12:40:27 +02003019static void disconnect_rsp(struct pending_cmd *cmd, void *data)
3020{
Szymon Jancc68fb7f2011-03-22 13:12:19 +01003021 struct mgmt_cp_disconnect *cp = cmd->param;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003022 struct sock **sk = data;
Johan Hedberga38528f2011-01-22 06:46:43 +02003023 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003024
Johan Hedberg88c3df12012-02-09 14:27:38 +02003025 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
3026 rp.addr.type = cp->addr.type;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003027
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003028 cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
3029 sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02003030
3031 *sk = cmd->sk;
3032 sock_hold(*sk);
3033
Johan Hedberga664b5b2011-02-19 12:06:02 -03003034 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003035}
3036
Johan Hedberg124f6e32012-02-09 13:50:12 +02003037static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberga8a1d192011-11-10 15:54:38 +02003038{
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003039 struct hci_dev *hdev = data;
Johan Hedberg124f6e32012-02-09 13:50:12 +02003040 struct mgmt_cp_unpair_device *cp = cmd->param;
3041 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02003042
3043 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02003044 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
3045 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02003046
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003047 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
3048
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003049 cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02003050
3051 mgmt_pending_remove(cmd);
3052}
3053
Johan Hedbergafc747a2012-01-15 18:11:07 +02003054int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
3055 u8 link_type, u8 addr_type)
Johan Hedbergf7520542011-01-20 12:34:39 +02003056{
Johan Hedberg4c659c32011-11-07 23:13:39 +02003057 struct mgmt_addr_info ev;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003058 struct sock *sk = NULL;
3059 int err;
3060
Johan Hedberg744cf192011-11-08 20:40:14 +02003061 mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
Johan Hedbergf7520542011-01-20 12:34:39 +02003062
Johan Hedbergf7520542011-01-20 12:34:39 +02003063 bacpy(&ev.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02003064 ev.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02003065
Johan Hedbergafc747a2012-01-15 18:11:07 +02003066 err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
3067 sk);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003068
3069 if (sk)
3070 sock_put(sk);
3071
Johan Hedberg124f6e32012-02-09 13:50:12 +02003072 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003073 hdev);
Johan Hedberga8a1d192011-11-10 15:54:38 +02003074
Johan Hedberg8962ee72011-01-20 12:40:27 +02003075 return err;
3076}
3077
Johan Hedberg88c3df12012-02-09 14:27:38 +02003078int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
3079 u8 link_type, u8 addr_type, u8 status)
Johan Hedberg8962ee72011-01-20 12:40:27 +02003080{
Johan Hedberg88c3df12012-02-09 14:27:38 +02003081 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003082 struct pending_cmd *cmd;
3083 int err;
3084
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003085 cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003086 if (!cmd)
3087 return -ENOENT;
3088
Johan Hedberg88c3df12012-02-09 14:27:38 +02003089 bacpy(&rp.addr.bdaddr, bdaddr);
3090 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg37d9ef72011-11-10 15:54:39 +02003091
Johan Hedberg88c3df12012-02-09 14:27:38 +02003092 err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003093 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02003094
Johan Hedberga664b5b2011-02-19 12:06:02 -03003095 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003096
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003097 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
3098 hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003099 return err;
Johan Hedbergf7520542011-01-20 12:34:39 +02003100}
Johan Hedberg17d5c042011-01-22 06:09:08 +02003101
Johan Hedberg48264f02011-11-09 13:58:58 +02003102int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3103 u8 addr_type, u8 status)
Johan Hedberg17d5c042011-01-22 06:09:08 +02003104{
3105 struct mgmt_ev_connect_failed ev;
3106
Johan Hedberg4c659c32011-11-07 23:13:39 +02003107 bacpy(&ev.addr.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02003108 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003109 ev.status = mgmt_status(status);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003110
Johan Hedberg744cf192011-11-08 20:40:14 +02003111 return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003112}
Johan Hedberg980e1a52011-01-22 06:10:07 +02003113
Johan Hedberg744cf192011-11-08 20:40:14 +02003114int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003115{
3116 struct mgmt_ev_pin_code_request ev;
3117
Johan Hedbergd8457692012-02-17 14:24:57 +02003118 bacpy(&ev.addr.bdaddr, bdaddr);
3119 ev.addr.type = MGMT_ADDR_BREDR;
Waldemar Rymarkiewicza770bb52011-04-28 12:07:59 +02003120 ev.secure = secure;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003121
Johan Hedberg744cf192011-11-08 20:40:14 +02003122 return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003123 NULL);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003124}
3125
Johan Hedberg744cf192011-11-08 20:40:14 +02003126int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3127 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003128{
3129 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003130 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003131 int err;
3132
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003133 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003134 if (!cmd)
3135 return -ENOENT;
3136
Johan Hedbergd8457692012-02-17 14:24:57 +02003137 bacpy(&rp.addr.bdaddr, bdaddr);
3138 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003139
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003140 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
3141 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003142
Johan Hedberga664b5b2011-02-19 12:06:02 -03003143 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003144
3145 return err;
3146}
3147
Johan Hedberg744cf192011-11-08 20:40:14 +02003148int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3149 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003150{
3151 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003152 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003153 int err;
3154
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003155 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003156 if (!cmd)
3157 return -ENOENT;
3158
Johan Hedbergd8457692012-02-17 14:24:57 +02003159 bacpy(&rp.addr.bdaddr, bdaddr);
3160 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003161
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003162 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
3163 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003164
Johan Hedberga664b5b2011-02-19 12:06:02 -03003165 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003166
3167 return err;
3168}
Johan Hedberga5c29682011-02-19 12:05:57 -03003169
Johan Hedberg744cf192011-11-08 20:40:14 +02003170int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003171 u8 link_type, u8 addr_type, __le32 value,
3172 u8 confirm_hint)
Johan Hedberga5c29682011-02-19 12:05:57 -03003173{
3174 struct mgmt_ev_user_confirm_request ev;
3175
Johan Hedberg744cf192011-11-08 20:40:14 +02003176 BT_DBG("%s", hdev->name);
Johan Hedberga5c29682011-02-19 12:05:57 -03003177
Johan Hedberg272d90d2012-02-09 15:26:12 +02003178 bacpy(&ev.addr.bdaddr, bdaddr);
3179 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg55bc1a32011-04-28 11:28:56 -07003180 ev.confirm_hint = confirm_hint;
Johan Hedberga5c29682011-02-19 12:05:57 -03003181 put_unaligned_le32(value, &ev.value);
3182
Johan Hedberg744cf192011-11-08 20:40:14 +02003183 return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003184 NULL);
Johan Hedberga5c29682011-02-19 12:05:57 -03003185}
3186
Johan Hedberg272d90d2012-02-09 15:26:12 +02003187int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
3188 u8 link_type, u8 addr_type)
Brian Gix604086b2011-11-23 08:28:33 -08003189{
3190 struct mgmt_ev_user_passkey_request ev;
3191
3192 BT_DBG("%s", hdev->name);
3193
Johan Hedberg272d90d2012-02-09 15:26:12 +02003194 bacpy(&ev.addr.bdaddr, bdaddr);
3195 ev.addr.type = link_to_mgmt(link_type, addr_type);
Brian Gix604086b2011-11-23 08:28:33 -08003196
3197 return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
3198 NULL);
3199}
3200
Brian Gix0df4c182011-11-16 13:53:13 -08003201static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003202 u8 link_type, u8 addr_type, u8 status,
3203 u8 opcode)
Johan Hedberga5c29682011-02-19 12:05:57 -03003204{
3205 struct pending_cmd *cmd;
3206 struct mgmt_rp_user_confirm_reply rp;
3207 int err;
3208
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003209 cmd = mgmt_pending_find(opcode, hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03003210 if (!cmd)
3211 return -ENOENT;
3212
Johan Hedberg272d90d2012-02-09 15:26:12 +02003213 bacpy(&rp.addr.bdaddr, bdaddr);
3214 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003215 err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
3216 &rp, sizeof(rp));
Johan Hedberga5c29682011-02-19 12:05:57 -03003217
Johan Hedberga664b5b2011-02-19 12:06:02 -03003218 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03003219
3220 return err;
3221}
3222
Johan Hedberg744cf192011-11-08 20:40:14 +02003223int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003224 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003225{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003226 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3227 status, MGMT_OP_USER_CONFIRM_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003228}
3229
Johan Hedberg272d90d2012-02-09 15:26:12 +02003230int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3231 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003232{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003233 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3234 status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003235}
Johan Hedberg2a611692011-02-19 12:06:00 -03003236
Brian Gix604086b2011-11-23 08:28:33 -08003237int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003238 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003239{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003240 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3241 status, MGMT_OP_USER_PASSKEY_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003242}
3243
Johan Hedberg272d90d2012-02-09 15:26:12 +02003244int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3245 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003246{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003247 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3248 status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003249}
3250
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003251int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3252 u8 addr_type, u8 status)
Johan Hedberg2a611692011-02-19 12:06:00 -03003253{
3254 struct mgmt_ev_auth_failed ev;
3255
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003256 bacpy(&ev.addr.bdaddr, bdaddr);
3257 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003258 ev.status = mgmt_status(status);
Johan Hedberg2a611692011-02-19 12:06:00 -03003259
Johan Hedberg744cf192011-11-08 20:40:14 +02003260 return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg2a611692011-02-19 12:06:00 -03003261}
Johan Hedbergb312b1612011-03-16 14:29:37 +02003262
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003263int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
3264{
3265 struct cmd_lookup match = { NULL, hdev };
3266 __le32 ev;
3267 int err;
3268
3269 if (status) {
3270 u8 mgmt_err = mgmt_status(status);
3271 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
3272 cmd_status_rsp, &mgmt_err);
3273 return 0;
3274 }
3275
3276 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
3277 &match);
3278
3279 ev = cpu_to_le32(get_current_settings(hdev));
3280 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3281
3282 if (match.sk)
3283 sock_put(match.sk);
3284
3285 return err;
3286}
3287
Johan Hedbergcacaf522012-02-21 00:52:42 +02003288static int clear_eir(struct hci_dev *hdev)
3289{
3290 struct hci_cp_write_eir cp;
3291
3292 if (!(hdev->features[6] & LMP_EXT_INQ))
3293 return 0;
3294
3295 memset(&cp, 0, sizeof(cp));
3296
3297 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
3298}
3299
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003300int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status)
3301{
3302 struct cmd_lookup match = { NULL, hdev };
3303 __le32 ev;
3304 int err;
3305
3306 if (status) {
3307 u8 mgmt_err = mgmt_status(status);
3308 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev,
3309 cmd_status_rsp, &mgmt_err);
3310 return 0;
3311 }
3312
3313 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
3314
3315 ev = cpu_to_le32(get_current_settings(hdev));
3316 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3317
Johan Hedbergcacaf522012-02-21 00:52:42 +02003318 if (match.sk) {
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003319 sock_put(match.sk);
3320
Johan Hedbergcacaf522012-02-21 00:52:42 +02003321 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
3322 update_eir(hdev);
3323 else
3324 clear_eir(hdev);
3325 }
3326
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003327 return err;
3328}
3329
Johan Hedberg744cf192011-11-08 20:40:14 +02003330int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
Johan Hedbergb312b1612011-03-16 14:29:37 +02003331{
3332 struct pending_cmd *cmd;
3333 struct mgmt_cp_set_local_name ev;
3334 int err;
3335
3336 memset(&ev, 0, sizeof(ev));
3337 memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
3338
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003339 cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02003340 if (!cmd)
3341 goto send_event;
3342
3343 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003344 err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02003345 mgmt_status(status));
Johan Hedbergb312b1612011-03-16 14:29:37 +02003346 goto failed;
3347 }
3348
Johan Hedberg744cf192011-11-08 20:40:14 +02003349 update_eir(hdev);
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03003350
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003351 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
Johan Hedbergb312b1612011-03-16 14:29:37 +02003352 sizeof(ev));
3353 if (err < 0)
3354 goto failed;
3355
3356send_event:
Johan Hedberg744cf192011-11-08 20:40:14 +02003357 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
Johan Hedbergb312b1612011-03-16 14:29:37 +02003358 cmd ? cmd->sk : NULL);
3359
3360failed:
3361 if (cmd)
3362 mgmt_pending_remove(cmd);
3363 return err;
3364}
Szymon Jancc35938b2011-03-22 13:12:21 +01003365
Johan Hedberg744cf192011-11-08 20:40:14 +02003366int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
3367 u8 *randomizer, u8 status)
Szymon Jancc35938b2011-03-22 13:12:21 +01003368{
3369 struct pending_cmd *cmd;
3370 int err;
3371
Johan Hedberg744cf192011-11-08 20:40:14 +02003372 BT_DBG("%s status %u", hdev->name, status);
Szymon Jancc35938b2011-03-22 13:12:21 +01003373
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003374 cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01003375 if (!cmd)
3376 return -ENOENT;
3377
3378 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003379 err = cmd_status(cmd->sk, hdev->id,
Johan Hedbergca69b792011-11-11 18:10:00 +02003380 MGMT_OP_READ_LOCAL_OOB_DATA,
3381 mgmt_status(status));
Szymon Jancc35938b2011-03-22 13:12:21 +01003382 } else {
3383 struct mgmt_rp_read_local_oob_data rp;
3384
3385 memcpy(rp.hash, hash, sizeof(rp.hash));
3386 memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
3387
Johan Hedberg744cf192011-11-08 20:40:14 +02003388 err = cmd_complete(cmd->sk, hdev->id,
3389 MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergaee9b2182012-02-18 15:07:59 +02003390 0, &rp, sizeof(rp));
Szymon Jancc35938b2011-03-22 13:12:21 +01003391 }
3392
3393 mgmt_pending_remove(cmd);
3394
3395 return err;
3396}
Johan Hedberge17acd42011-03-30 23:57:16 +03003397
Johan Hedberg48264f02011-11-09 13:58:58 +02003398int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg561aafb2012-01-04 13:31:59 +02003399 u8 addr_type, u8 *dev_class, s8 rssi,
Johan Hedberge319d2e2012-01-15 19:51:59 +02003400 u8 cfm_name, u8 *eir, u16 eir_len)
Johan Hedberge17acd42011-03-30 23:57:16 +03003401{
Johan Hedberge319d2e2012-01-15 19:51:59 +02003402 char buf[512];
3403 struct mgmt_ev_device_found *ev = (void *) buf;
Johan Hedberg1dc06092012-01-15 21:01:23 +02003404 size_t ev_size;
Johan Hedberge17acd42011-03-30 23:57:16 +03003405
Johan Hedberg1dc06092012-01-15 21:01:23 +02003406 /* Leave 5 bytes for a potential CoD field */
3407 if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
Andre Guedes7d262f82012-01-10 18:20:49 -03003408 return -EINVAL;
3409
Johan Hedberg1dc06092012-01-15 21:01:23 +02003410 memset(buf, 0, sizeof(buf));
3411
Johan Hedberge319d2e2012-01-15 19:51:59 +02003412 bacpy(&ev->addr.bdaddr, bdaddr);
3413 ev->addr.type = link_to_mgmt(link_type, addr_type);
3414 ev->rssi = rssi;
3415 ev->confirm_name = cfm_name;
Johan Hedberge17acd42011-03-30 23:57:16 +03003416
Johan Hedberg1dc06092012-01-15 21:01:23 +02003417 if (eir_len > 0)
Johan Hedberge319d2e2012-01-15 19:51:59 +02003418 memcpy(ev->eir, eir, eir_len);
Johan Hedberge17acd42011-03-30 23:57:16 +03003419
Johan Hedberg1dc06092012-01-15 21:01:23 +02003420 if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
3421 eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
3422 dev_class, 3);
3423
3424 put_unaligned_le16(eir_len, &ev->eir_len);
3425
3426 ev_size = sizeof(*ev) + eir_len;
Andre Guedesf8523592011-09-09 18:56:26 -03003427
Johan Hedberge319d2e2012-01-15 19:51:59 +02003428 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
Johan Hedberge17acd42011-03-30 23:57:16 +03003429}
Johan Hedberga88a9652011-03-30 13:18:12 +03003430
Johan Hedbergb644ba32012-01-17 21:48:47 +02003431int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3432 u8 addr_type, s8 rssi, u8 *name, u8 name_len)
Johan Hedberga88a9652011-03-30 13:18:12 +03003433{
Johan Hedbergb644ba32012-01-17 21:48:47 +02003434 struct mgmt_ev_device_found *ev;
3435 char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
3436 u16 eir_len;
Johan Hedberga88a9652011-03-30 13:18:12 +03003437
Johan Hedbergb644ba32012-01-17 21:48:47 +02003438 ev = (struct mgmt_ev_device_found *) buf;
Johan Hedberga88a9652011-03-30 13:18:12 +03003439
Johan Hedbergb644ba32012-01-17 21:48:47 +02003440 memset(buf, 0, sizeof(buf));
Johan Hedberga88a9652011-03-30 13:18:12 +03003441
Johan Hedbergb644ba32012-01-17 21:48:47 +02003442 bacpy(&ev->addr.bdaddr, bdaddr);
3443 ev->addr.type = link_to_mgmt(link_type, addr_type);
3444 ev->rssi = rssi;
3445
3446 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
3447 name_len);
3448
3449 put_unaligned_le16(eir_len, &ev->eir_len);
3450
Johan Hedberg053c7e02012-02-04 00:06:00 +02003451 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
3452 sizeof(*ev) + eir_len, NULL);
Johan Hedberga88a9652011-03-30 13:18:12 +03003453}
Johan Hedberg314b2382011-04-27 10:29:57 -04003454
Andre Guedes7a135102011-11-09 17:14:25 -03003455int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
Johan Hedberg164a6e72011-11-01 17:06:44 +02003456{
3457 struct pending_cmd *cmd;
Johan Hedbergf808e162012-02-19 12:52:07 +02003458 u8 type;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003459 int err;
3460
Andre Guedes203159d2012-02-13 15:41:01 -03003461 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
3462
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003463 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003464 if (!cmd)
3465 return -ENOENT;
3466
Johan Hedbergf808e162012-02-19 12:52:07 +02003467 type = hdev->discovery.type;
3468
3469 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3470 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003471 mgmt_pending_remove(cmd);
3472
3473 return err;
3474}
3475
Andre Guedese6d465c2011-11-09 17:14:26 -03003476int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
3477{
3478 struct pending_cmd *cmd;
3479 int err;
3480
3481 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
3482 if (!cmd)
3483 return -ENOENT;
3484
Johan Hedbergd9306502012-02-20 23:25:18 +02003485 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3486 &hdev->discovery.type,
3487 sizeof(hdev->discovery.type));
Johan Hedberg03811012010-12-08 00:21:06 +02003488 mgmt_pending_remove(cmd);
3489
3490 return err;
3491}
Johan Hedberg314b2382011-04-27 10:29:57 -04003492
Johan Hedberg744cf192011-11-08 20:40:14 +02003493int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
Johan Hedberg314b2382011-04-27 10:29:57 -04003494{
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003495 struct mgmt_ev_discovering ev;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003496 struct pending_cmd *cmd;
3497
Andre Guedes343fb142011-11-22 17:14:19 -03003498 BT_DBG("%s discovering %u", hdev->name, discovering);
3499
Johan Hedberg164a6e72011-11-01 17:06:44 +02003500 if (discovering)
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003501 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003502 else
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003503 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003504
3505 if (cmd != NULL) {
Johan Hedbergf808e162012-02-19 12:52:07 +02003506 u8 type = hdev->discovery.type;
3507
Johan Hedbergd9306502012-02-20 23:25:18 +02003508 cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0,
Johan Hedbergf808e162012-02-19 12:52:07 +02003509 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003510 mgmt_pending_remove(cmd);
3511 }
3512
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003513 memset(&ev, 0, sizeof(ev));
3514 ev.type = hdev->discovery.type;
3515 ev.discovering = discovering;
3516
3517 return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg314b2382011-04-27 10:29:57 -04003518}
Antti Julku5e762442011-08-25 16:48:02 +03003519
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003520int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003521{
3522 struct pending_cmd *cmd;
3523 struct mgmt_ev_device_blocked ev;
3524
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003525 cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003526
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003527 bacpy(&ev.addr.bdaddr, bdaddr);
3528 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003529
Johan Hedberg744cf192011-11-08 20:40:14 +02003530 return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
3531 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003532}
3533
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003534int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003535{
3536 struct pending_cmd *cmd;
3537 struct mgmt_ev_device_unblocked ev;
3538
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003539 cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003540
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003541 bacpy(&ev.addr.bdaddr, bdaddr);
3542 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003543
Johan Hedberg744cf192011-11-08 20:40:14 +02003544 return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
3545 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003546}
Marcel Holtmannd7b7e792012-02-20 21:47:49 +01003547
3548module_param(enable_hs, bool, 0644);
3549MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
3550
3551module_param(enable_le, bool, 0644);
3552MODULE_PARM_DESC(enable_le, "Enable Low Energy support");