blob: a2c2e12516c6cc9cbb99963d75db0471dc7a0575 [file] [log] [blame]
Johan Hedberg03811012010-12-08 00:21:06 +02001/*
2 BlueZ - Bluetooth protocol stack for Linux
3 Copyright (C) 2010 Nokia Corporation
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License version 2 as
7 published by the Free Software Foundation;
8
9 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
13 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
18 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
19 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
20 SOFTWARE IS DISCLAIMED.
21*/
22
23/* Bluetooth HCI Management interface */
24
Johan Hedbergca69b792011-11-11 18:10:00 +020025#include <linux/kernel.h>
Szymon Janc72359752011-02-17 14:16:32 +010026#include <linux/uaccess.h>
Paul Gortmaker3a9a2312011-05-27 09:12:25 -040027#include <linux/module.h>
Johan Hedberg03811012010-12-08 00:21:06 +020028#include <asm/unaligned.h>
29
30#include <net/bluetooth/bluetooth.h>
31#include <net/bluetooth/hci_core.h>
32#include <net/bluetooth/mgmt.h>
Brian Gix5fe57d92011-12-21 16:12:13 -080033#include <net/bluetooth/smp.h>
Johan Hedberg03811012010-12-08 00:21:06 +020034
Johan Hedberg02d98122010-12-13 21:07:04 +020035#define MGMT_VERSION 0
36#define MGMT_REVISION 1
37
Andre Guedes3fd24152012-02-03 17:48:01 -030038/*
39 * These LE scan and inquiry parameters were chosen according to LE General
40 * Discovery Procedure specification.
41 */
42#define LE_SCAN_TYPE 0x01
43#define LE_SCAN_WIN 0x12
44#define LE_SCAN_INT 0x12
45#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */
46
Andre Guedese8777522012-02-03 17:48:02 -030047#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
Andre Guedes2519a1f2011-11-07 11:45:24 -030048
Johan Hedberg7d785252011-12-15 00:47:39 +020049#define SERVICE_CACHE_TIMEOUT (5 * 1000)
50
Johan Hedbergeec8d2b2010-12-16 10:17:38 +020051struct pending_cmd {
52 struct list_head list;
Johan Hedbergfc2f4b12011-11-09 13:58:56 +020053 u16 opcode;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +020054 int index;
Szymon Jancc68fb7f2011-03-22 13:12:19 +010055 void *param;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +020056 struct sock *sk;
Johan Hedberge9a416b2011-02-19 12:05:56 -030057 void *user_data;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +020058};
59
Johan Hedbergca69b792011-11-11 18:10:00 +020060/* HCI to MGMT error code conversion table */
61static u8 mgmt_status_table[] = {
62 MGMT_STATUS_SUCCESS,
63 MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */
64 MGMT_STATUS_NOT_CONNECTED, /* No Connection */
65 MGMT_STATUS_FAILED, /* Hardware Failure */
66 MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */
67 MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */
68 MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */
69 MGMT_STATUS_NO_RESOURCES, /* Memory Full */
70 MGMT_STATUS_TIMEOUT, /* Connection Timeout */
71 MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */
72 MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */
73 MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */
74 MGMT_STATUS_BUSY, /* Command Disallowed */
75 MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */
76 MGMT_STATUS_REJECTED, /* Rejected Security */
77 MGMT_STATUS_REJECTED, /* Rejected Personal */
78 MGMT_STATUS_TIMEOUT, /* Host Timeout */
79 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */
80 MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */
81 MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */
82 MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */
83 MGMT_STATUS_DISCONNECTED, /* OE Power Off */
84 MGMT_STATUS_DISCONNECTED, /* Connection Terminated */
85 MGMT_STATUS_BUSY, /* Repeated Attempts */
86 MGMT_STATUS_REJECTED, /* Pairing Not Allowed */
87 MGMT_STATUS_FAILED, /* Unknown LMP PDU */
88 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */
89 MGMT_STATUS_REJECTED, /* SCO Offset Rejected */
90 MGMT_STATUS_REJECTED, /* SCO Interval Rejected */
91 MGMT_STATUS_REJECTED, /* Air Mode Rejected */
92 MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */
93 MGMT_STATUS_FAILED, /* Unspecified Error */
94 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */
95 MGMT_STATUS_FAILED, /* Role Change Not Allowed */
96 MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */
97 MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */
98 MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */
99 MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */
100 MGMT_STATUS_FAILED, /* Unit Link Key Used */
101 MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */
102 MGMT_STATUS_TIMEOUT, /* Instant Passed */
103 MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */
104 MGMT_STATUS_FAILED, /* Transaction Collision */
105 MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */
106 MGMT_STATUS_REJECTED, /* QoS Rejected */
107 MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */
108 MGMT_STATUS_REJECTED, /* Insufficient Security */
109 MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */
110 MGMT_STATUS_BUSY, /* Role Switch Pending */
111 MGMT_STATUS_FAILED, /* Slot Violation */
112 MGMT_STATUS_FAILED, /* Role Switch Failed */
113 MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */
114 MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */
115 MGMT_STATUS_BUSY, /* Host Busy Pairing */
116 MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */
117 MGMT_STATUS_BUSY, /* Controller Busy */
118 MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */
119 MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */
120 MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */
121 MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */
122 MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */
123};
124
125static u8 mgmt_status(u8 hci_status)
126{
127 if (hci_status < ARRAY_SIZE(mgmt_status_table))
128 return mgmt_status_table[hci_status];
129
130 return MGMT_STATUS_FAILED;
131}
132
Szymon Janc4e51eae2011-02-25 19:05:48 +0100133static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200134{
135 struct sk_buff *skb;
136 struct mgmt_hdr *hdr;
137 struct mgmt_ev_cmd_status *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300138 int err;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200139
Szymon Janc34eb5252011-02-28 14:10:08 +0100140 BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200141
142 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
143 if (!skb)
144 return -ENOMEM;
145
146 hdr = (void *) skb_put(skb, sizeof(*hdr));
147
148 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100149 hdr->index = cpu_to_le16(index);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200150 hdr->len = cpu_to_le16(sizeof(*ev));
151
152 ev = (void *) skb_put(skb, sizeof(*ev));
153 ev->status = status;
154 put_unaligned_le16(cmd, &ev->opcode);
155
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300156 err = sock_queue_rcv_skb(sk, skb);
157 if (err < 0)
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200158 kfree_skb(skb);
159
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300160 return err;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200161}
162
Szymon Janc4e51eae2011-02-25 19:05:48 +0100163static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
164 size_t rp_len)
Johan Hedberg02d98122010-12-13 21:07:04 +0200165{
166 struct sk_buff *skb;
167 struct mgmt_hdr *hdr;
168 struct mgmt_ev_cmd_complete *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300169 int err;
Johan Hedberg02d98122010-12-13 21:07:04 +0200170
171 BT_DBG("sock %p", sk);
172
Johan Hedberga38528f2011-01-22 06:46:43 +0200173 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC);
Johan Hedberg02d98122010-12-13 21:07:04 +0200174 if (!skb)
175 return -ENOMEM;
176
177 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedberga38528f2011-01-22 06:46:43 +0200178
Johan Hedberg02d98122010-12-13 21:07:04 +0200179 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100180 hdr->index = cpu_to_le16(index);
Johan Hedberga38528f2011-01-22 06:46:43 +0200181 hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200182
Johan Hedberga38528f2011-01-22 06:46:43 +0200183 ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
184 put_unaligned_le16(cmd, &ev->opcode);
Szymon Janc8020c162011-02-28 14:09:50 +0100185
186 if (rp)
187 memcpy(ev->data, rp, rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200188
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300189 err = sock_queue_rcv_skb(sk, skb);
190 if (err < 0)
Johan Hedberg02d98122010-12-13 21:07:04 +0200191 kfree_skb(skb);
192
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300193 return err;;
Johan Hedberg02d98122010-12-13 21:07:04 +0200194}
195
Johan Hedberga38528f2011-01-22 06:46:43 +0200196static int read_version(struct sock *sk)
197{
198 struct mgmt_rp_read_version rp;
199
200 BT_DBG("sock %p", sk);
201
202 rp.version = MGMT_VERSION;
203 put_unaligned_le16(MGMT_REVISION, &rp.revision);
204
Szymon Janc4e51eae2011-02-25 19:05:48 +0100205 return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, &rp,
206 sizeof(rp));
Johan Hedberga38528f2011-01-22 06:46:43 +0200207}
208
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200209static int read_index_list(struct sock *sk)
210{
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200211 struct mgmt_rp_read_index_list *rp;
212 struct list_head *p;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200213 struct hci_dev *d;
Johan Hedberga38528f2011-01-22 06:46:43 +0200214 size_t rp_len;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200215 u16 count;
Johan Hedberga38528f2011-01-22 06:46:43 +0200216 int i, err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200217
218 BT_DBG("sock %p", sk);
219
220 read_lock(&hci_dev_list_lock);
221
222 count = 0;
223 list_for_each(p, &hci_dev_list) {
224 count++;
225 }
226
Johan Hedberga38528f2011-01-22 06:46:43 +0200227 rp_len = sizeof(*rp) + (2 * count);
228 rp = kmalloc(rp_len, GFP_ATOMIC);
229 if (!rp) {
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100230 read_unlock(&hci_dev_list_lock);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200231 return -ENOMEM;
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100232 }
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200233
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200234 put_unaligned_le16(count, &rp->num_controllers);
235
236 i = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200237 list_for_each_entry(d, &hci_dev_list, list) {
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200238 if (test_and_clear_bit(HCI_AUTO_OFF, &d->dev_flags))
Johan Hedberge0f93092011-11-09 01:44:22 +0200239 cancel_delayed_work(&d->power_off);
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200240
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200241 if (test_bit(HCI_SETUP, &d->dev_flags))
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200242 continue;
243
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200244 put_unaligned_le16(d->id, &rp->index[i++]);
245 BT_DBG("Added hci%u", d->id);
246 }
247
248 read_unlock(&hci_dev_list_lock);
249
Szymon Janc4e51eae2011-02-25 19:05:48 +0100250 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, rp,
251 rp_len);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200252
Johan Hedberga38528f2011-01-22 06:46:43 +0200253 kfree(rp);
254
255 return err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200256}
257
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200258static u32 get_supported_settings(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +0200259{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200260 u32 settings = 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200261
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200262 settings |= MGMT_SETTING_POWERED;
263 settings |= MGMT_SETTING_CONNECTABLE;
264 settings |= MGMT_SETTING_FAST_CONNECTABLE;
265 settings |= MGMT_SETTING_DISCOVERABLE;
266 settings |= MGMT_SETTING_PAIRABLE;
Johan Hedberg03811012010-12-08 00:21:06 +0200267
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200268 if (hdev->features[6] & LMP_SIMPLE_PAIR)
269 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200270
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200271 if (!(hdev->features[4] & LMP_NO_BREDR)) {
272 settings |= MGMT_SETTING_BREDR;
273 settings |= MGMT_SETTING_LINK_SECURITY;
274 }
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200275
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200276 if (hdev->features[4] & LMP_LE)
277 settings |= MGMT_SETTING_LE;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200278
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200279 return settings;
280}
Johan Hedbergebc99fe2011-01-04 11:54:26 +0200281
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200282static u32 get_current_settings(struct hci_dev *hdev)
283{
284 u32 settings = 0;
Johan Hedbergdc4fe302011-03-16 14:29:36 +0200285
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200286 if (test_bit(HCI_UP, &hdev->flags))
287 settings |= MGMT_SETTING_POWERED;
288 else
289 return settings;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200290
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200291 if (test_bit(HCI_PSCAN, &hdev->flags))
292 settings |= MGMT_SETTING_CONNECTABLE;
293
294 if (test_bit(HCI_ISCAN, &hdev->flags))
295 settings |= MGMT_SETTING_DISCOVERABLE;
296
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200297 if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200298 settings |= MGMT_SETTING_PAIRABLE;
299
300 if (!(hdev->features[4] & LMP_NO_BREDR))
301 settings |= MGMT_SETTING_BREDR;
302
Andre Guedes59e29402011-12-30 10:34:03 -0300303 if (hdev->host_features[0] & LMP_HOST_LE)
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200304 settings |= MGMT_SETTING_LE;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200305
306 if (test_bit(HCI_AUTH, &hdev->flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200307 settings |= MGMT_SETTING_LINK_SECURITY;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200308
Johan Hedberg84bde9d6c2012-01-25 14:21:06 +0200309 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200310 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200311
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200312 return settings;
Johan Hedbergc542a062011-01-26 13:11:03 +0200313}
314
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300315#define PNP_INFO_SVCLASS_ID 0x1200
316
317static u8 bluetooth_base_uuid[] = {
318 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
319 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320};
321
322static u16 get_uuid16(u8 *uuid128)
323{
324 u32 val;
325 int i;
326
327 for (i = 0; i < 12; i++) {
328 if (bluetooth_base_uuid[i] != uuid128[i])
329 return 0;
330 }
331
332 memcpy(&val, &uuid128[12], 4);
333
334 val = le32_to_cpu(val);
335 if (val > 0xffff)
336 return 0;
337
338 return (u16) val;
339}
340
341static void create_eir(struct hci_dev *hdev, u8 *data)
342{
343 u8 *ptr = data;
344 u16 eir_len = 0;
345 u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
346 int i, truncated = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200347 struct bt_uuid *uuid;
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300348 size_t name_len;
349
350 name_len = strlen(hdev->dev_name);
351
352 if (name_len > 0) {
353 /* EIR Data type */
354 if (name_len > 48) {
355 name_len = 48;
356 ptr[1] = EIR_NAME_SHORT;
357 } else
358 ptr[1] = EIR_NAME_COMPLETE;
359
360 /* EIR Data length */
361 ptr[0] = name_len + 1;
362
363 memcpy(ptr + 2, hdev->dev_name, name_len);
364
365 eir_len += (name_len + 2);
366 ptr += (name_len + 2);
367 }
368
369 memset(uuid16_list, 0, sizeof(uuid16_list));
370
371 /* Group all UUID16 types */
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200372 list_for_each_entry(uuid, &hdev->uuids, list) {
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300373 u16 uuid16;
374
375 uuid16 = get_uuid16(uuid->uuid);
376 if (uuid16 == 0)
377 return;
378
379 if (uuid16 < 0x1100)
380 continue;
381
382 if (uuid16 == PNP_INFO_SVCLASS_ID)
383 continue;
384
385 /* Stop if not enough space to put next UUID */
386 if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
387 truncated = 1;
388 break;
389 }
390
391 /* Check for duplicates */
392 for (i = 0; uuid16_list[i] != 0; i++)
393 if (uuid16_list[i] == uuid16)
394 break;
395
396 if (uuid16_list[i] == 0) {
397 uuid16_list[i] = uuid16;
398 eir_len += sizeof(u16);
399 }
400 }
401
402 if (uuid16_list[0] != 0) {
403 u8 *length = ptr;
404
405 /* EIR Data type */
406 ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
407
408 ptr += 2;
409 eir_len += 2;
410
411 for (i = 0; uuid16_list[i] != 0; i++) {
412 *ptr++ = (uuid16_list[i] & 0x00ff);
413 *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
414 }
415
416 /* EIR Data length */
417 *length = (i * sizeof(u16)) + 1;
418 }
419}
420
421static int update_eir(struct hci_dev *hdev)
422{
423 struct hci_cp_write_eir cp;
424
425 if (!(hdev->features[6] & LMP_EXT_INQ))
426 return 0;
427
Johan Hedberg84bde9d6c2012-01-25 14:21:06 +0200428 if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300429 return 0;
430
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200431 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300432 return 0;
433
434 memset(&cp, 0, sizeof(cp));
435
436 create_eir(hdev, cp.data);
437
438 if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
439 return 0;
440
441 memcpy(hdev->eir, cp.data, sizeof(cp.data));
442
443 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
444}
445
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200446static u8 get_service_classes(struct hci_dev *hdev)
447{
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300448 struct bt_uuid *uuid;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200449 u8 val = 0;
450
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300451 list_for_each_entry(uuid, &hdev->uuids, list)
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200452 val |= uuid->svc_hint;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200453
454 return val;
455}
456
457static int update_class(struct hci_dev *hdev)
458{
459 u8 cod[3];
460
461 BT_DBG("%s", hdev->name);
462
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200463 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200464 return 0;
465
466 cod[0] = hdev->minor_class;
467 cod[1] = hdev->major_class;
468 cod[2] = get_service_classes(hdev);
469
470 if (memcmp(cod, hdev->dev_class, 3) == 0)
471 return 0;
472
473 return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
474}
475
Johan Hedberg7d785252011-12-15 00:47:39 +0200476static void service_cache_off(struct work_struct *work)
477{
478 struct hci_dev *hdev = container_of(work, struct hci_dev,
479 service_cache.work);
480
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200481 if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200482 return;
483
484 hci_dev_lock(hdev);
485
486 update_eir(hdev);
487 update_class(hdev);
488
489 hci_dev_unlock(hdev);
490}
491
492static void mgmt_init_hdev(struct hci_dev *hdev)
493{
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200494 if (!test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200495 INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
496
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200497 if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200498 schedule_delayed_work(&hdev->service_cache,
499 msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
500}
501
Johan Hedberg03811012010-12-08 00:21:06 +0200502static int read_controller_info(struct sock *sk, u16 index)
503{
504 struct mgmt_rp_read_info rp;
505 struct hci_dev *hdev;
506
507 BT_DBG("sock %p hci%u", sk, index);
508
509 hdev = hci_dev_get(index);
510 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200511 return cmd_status(sk, index, MGMT_OP_READ_INFO,
512 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200513
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200514 if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
Johan Hedberg03811012010-12-08 00:21:06 +0200515 cancel_delayed_work_sync(&hdev->power_off);
516
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300517 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200518
Johan Hedberg7d785252011-12-15 00:47:39 +0200519 if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
520 mgmt_init_hdev(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200521
522 memset(&rp, 0, sizeof(rp));
523
Johan Hedberg03811012010-12-08 00:21:06 +0200524 bacpy(&rp.bdaddr, &hdev->bdaddr);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200525
526 rp.version = hdev->hci_ver;
527
Johan Hedberg03811012010-12-08 00:21:06 +0200528 put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200529
530 rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
531 rp.current_settings = cpu_to_le32(get_current_settings(hdev));
532
533 memcpy(rp.dev_class, hdev->dev_class, 3);
Johan Hedberg03811012010-12-08 00:21:06 +0200534
535 memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
536
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300537 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200538 hci_dev_put(hdev);
539
540 return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
541}
542
543static void mgmt_pending_free(struct pending_cmd *cmd)
544{
545 sock_put(cmd->sk);
546 kfree(cmd->param);
547 kfree(cmd);
548}
549
550static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
551 struct hci_dev *hdev,
552 void *data, u16 len)
553{
554 struct pending_cmd *cmd;
555
556 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
557 if (!cmd)
558 return NULL;
559
560 cmd->opcode = opcode;
561 cmd->index = hdev->id;
562
563 cmd->param = kmalloc(len, GFP_ATOMIC);
564 if (!cmd->param) {
565 kfree(cmd);
566 return NULL;
567 }
568
569 if (data)
570 memcpy(cmd->param, data, len);
571
572 cmd->sk = sk;
573 sock_hold(sk);
574
575 list_add(&cmd->list, &hdev->mgmt_pending);
576
577 return cmd;
578}
579
580static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
581 void (*cb)(struct pending_cmd *cmd, void *data),
582 void *data)
583{
584 struct list_head *p, *n;
585
586 list_for_each_safe(p, n, &hdev->mgmt_pending) {
587 struct pending_cmd *cmd;
588
589 cmd = list_entry(p, struct pending_cmd, list);
590
591 if (opcode > 0 && cmd->opcode != opcode)
592 continue;
593
594 cb(cmd, data);
595 }
596}
597
598static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
599{
600 struct pending_cmd *cmd;
601
602 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
603 if (cmd->opcode == opcode)
604 return cmd;
605 }
606
607 return NULL;
608}
609
610static void mgmt_pending_remove(struct pending_cmd *cmd)
611{
612 list_del(&cmd->list);
613 mgmt_pending_free(cmd);
614}
615
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200616static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
Johan Hedberg86805702011-11-11 16:18:52 +0200617{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200618 __le32 settings = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg86805702011-11-11 16:18:52 +0200619
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200620 return cmd_complete(sk, hdev->id, opcode, &settings, sizeof(settings));
Johan Hedberg86805702011-11-11 16:18:52 +0200621}
622
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300623static int set_powered(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200624{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300625 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200626 struct hci_dev *hdev;
627 struct pending_cmd *cmd;
628 int err, up;
629
Johan Hedberg03811012010-12-08 00:21:06 +0200630 BT_DBG("request for hci%u", index);
631
632 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200633 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
634 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200635
636 hdev = hci_dev_get(index);
637 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200638 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
639 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200640
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300641 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200642
643 up = test_bit(HCI_UP, &hdev->flags);
644 if ((cp->val && up) || (!cp->val && !up)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200645 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200646 goto failed;
647 }
648
649 if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200650 err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
651 MGMT_STATUS_BUSY);
Johan Hedberg03811012010-12-08 00:21:06 +0200652 goto failed;
653 }
654
655 cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
656 if (!cmd) {
657 err = -ENOMEM;
658 goto failed;
659 }
660
661 if (cp->val)
Gustavo F. Padovan7f971042011-12-18 12:40:32 -0200662 schedule_work(&hdev->power_on);
Johan Hedberg03811012010-12-08 00:21:06 +0200663 else
Gustavo F. Padovan80b7ab32011-12-17 14:52:27 -0200664 schedule_work(&hdev->power_off.work);
Johan Hedberg03811012010-12-08 00:21:06 +0200665
666 err = 0;
667
668failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300669 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200670 hci_dev_put(hdev);
671 return err;
672}
673
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300674static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200675{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300676 struct mgmt_cp_set_discoverable *cp = data;
Johan Hedberge41d8b42010-12-13 21:07:03 +0200677 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +0200678 struct pending_cmd *cmd;
679 u8 scan;
680 int err;
681
Johan Hedberg03811012010-12-08 00:21:06 +0200682 BT_DBG("request for hci%u", index);
683
684 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200685 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
686 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200687
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200688 hdev = hci_dev_get(index);
689 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200690 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
691 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200692
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300693 hci_dev_lock(hdev);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200694
695 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200696 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
697 MGMT_STATUS_NOT_POWERED);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200698 goto failed;
699 }
700
701 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
702 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200703 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
704 MGMT_STATUS_BUSY);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200705 goto failed;
706 }
707
708 if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
709 test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200710 err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200711 goto failed;
712 }
713
714 cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
715 if (!cmd) {
716 err = -ENOMEM;
717 goto failed;
718 }
719
720 scan = SCAN_PAGE;
721
722 if (cp->val)
723 scan |= SCAN_INQUIRY;
724 else
725 cancel_delayed_work(&hdev->discov_off);
726
727 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
728 if (err < 0)
729 mgmt_pending_remove(cmd);
730
Johan Hedberg03811012010-12-08 00:21:06 +0200731 if (cp->val)
732 hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
733
Johan Hedberge41d8b42010-12-13 21:07:03 +0200734failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300735 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200736 hci_dev_put(hdev);
737
738 return err;
739}
740
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300741static int set_connectable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200742{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300743 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200744 struct hci_dev *hdev;
745 struct pending_cmd *cmd;
746 u8 scan;
747 int err;
748
Johan Hedberge41d8b42010-12-13 21:07:03 +0200749 BT_DBG("request for hci%u", index);
750
Johan Hedberg03811012010-12-08 00:21:06 +0200751 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200752 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
753 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +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_CONNECTABLE,
758 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200759
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300760 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200761
762 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200763 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
764 MGMT_STATUS_NOT_POWERED);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200765 goto failed;
766 }
767
768 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
769 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200770 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
771 MGMT_STATUS_BUSY);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200772 goto failed;
773 }
774
775 if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200776 err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200777 goto failed;
778 }
779
780 cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
781 if (!cmd) {
782 err = -ENOMEM;
783 goto failed;
784 }
785
786 if (cp->val)
787 scan = SCAN_PAGE;
788 else
789 scan = 0;
790
791 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
792 if (err < 0)
793 mgmt_pending_remove(cmd);
794
795failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300796 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200797 hci_dev_put(hdev);
798
799 return err;
800}
801
802static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
803 u16 data_len, struct sock *skip_sk)
804{
805 struct sk_buff *skb;
806 struct mgmt_hdr *hdr;
807
808 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
809 if (!skb)
810 return -ENOMEM;
811
812 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
813
814 hdr = (void *) skb_put(skb, sizeof(*hdr));
815 hdr->opcode = cpu_to_le16(event);
816 if (hdev)
817 hdr->index = cpu_to_le16(hdev->id);
818 else
819 hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
820 hdr->len = cpu_to_le16(data_len);
821
822 if (data)
823 memcpy(skb_put(skb, data_len), data, data_len);
824
825 hci_send_to_sock(NULL, skb, skip_sk);
826 kfree_skb(skb);
827
828 return 0;
829}
830
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300831static int set_pairable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg73f22f62010-12-29 16:00:25 +0200832{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300833 struct mgmt_mode *cp = data;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200834 struct hci_dev *hdev;
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200835 __le32 ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200836 int err;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200837
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200838 BT_DBG("request for hci%u", index);
839
840 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200841 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
842 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200843
844 hdev = hci_dev_get(index);
845 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200846 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
847 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200848
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300849 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200850
851 if (cp->val)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200852 set_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200853 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200854 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200855
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200856 err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200857 if (err < 0)
858 goto failed;
859
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200860 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200861
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200862 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200863
864failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300865 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200866 hci_dev_put(hdev);
867
868 return err;
869}
Johan Hedberg72a734e2010-12-30 00:38:22 +0200870
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300871static int add_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200872{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300873 struct mgmt_cp_add_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200874 struct hci_dev *hdev;
875 struct bt_uuid *uuid;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200876 int err;
877
Szymon Janc4e51eae2011-02-25 19:05:48 +0100878 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200879
Szymon Jancbdce7ba2011-02-25 19:05:49 +0100880 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200881 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
882 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +0100883
Szymon Janc4e51eae2011-02-25 19:05:48 +0100884 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200885 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200886 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
887 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200888
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300889 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200890
891 uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
892 if (!uuid) {
893 err = -ENOMEM;
894 goto failed;
895 }
896
897 memcpy(uuid->uuid, cp->uuid, 16);
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200898 uuid->svc_hint = cp->svc_hint;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200899
900 list_add(&uuid->list, &hdev->uuids);
901
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200902 err = update_class(hdev);
903 if (err < 0)
904 goto failed;
905
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300906 err = update_eir(hdev);
907 if (err < 0)
908 goto failed;
909
Szymon Janc4e51eae2011-02-25 19:05:48 +0100910 err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200911
912failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300913 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200914 hci_dev_put(hdev);
915
916 return err;
917}
918
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300919static int remove_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200920{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300921 struct mgmt_cp_remove_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200922 struct list_head *p, *n;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200923 struct hci_dev *hdev;
924 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 +0200925 int err, found;
926
Szymon Janc4e51eae2011-02-25 19:05:48 +0100927 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200928
Szymon Jancbdce7ba2011-02-25 19:05:49 +0100929 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200930 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
931 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +0100932
Szymon Janc4e51eae2011-02-25 19:05:48 +0100933 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200934 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200935 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
936 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200937
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300938 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200939
940 if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
941 err = hci_uuids_clear(hdev);
942 goto unlock;
943 }
944
945 found = 0;
946
947 list_for_each_safe(p, n, &hdev->uuids) {
948 struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
949
950 if (memcmp(match->uuid, cp->uuid, 16) != 0)
951 continue;
952
953 list_del(&match->list);
954 found++;
955 }
956
957 if (found == 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200958 err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
959 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200960 goto unlock;
961 }
962
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200963 err = update_class(hdev);
964 if (err < 0)
965 goto unlock;
966
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300967 err = update_eir(hdev);
968 if (err < 0)
969 goto unlock;
970
Szymon Janc4e51eae2011-02-25 19:05:48 +0100971 err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200972
973unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300974 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +0200975 hci_dev_put(hdev);
976
977 return err;
978}
979
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300980static int set_dev_class(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200981{
982 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300983 struct mgmt_cp_set_dev_class *cp = data;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200984 int err;
985
Szymon Janc4e51eae2011-02-25 19:05:48 +0100986 BT_DBG("request for hci%u", index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200987
Szymon Jancbdce7ba2011-02-25 19:05:49 +0100988 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200989 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
990 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +0100991
Szymon Janc4e51eae2011-02-25 19:05:48 +0100992 hdev = hci_dev_get(index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200993 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200994 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
995 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200996
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300997 hci_dev_lock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200998
999 hdev->major_class = cp->major;
1000 hdev->minor_class = cp->minor;
1001
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001002 if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
Johan Hedberg7d785252011-12-15 00:47:39 +02001003 hci_dev_unlock(hdev);
1004 cancel_delayed_work_sync(&hdev->service_cache);
1005 hci_dev_lock(hdev);
Johan Hedberg14c0b602011-12-15 00:47:37 +02001006 update_eir(hdev);
Johan Hedberg7d785252011-12-15 00:47:39 +02001007 }
Johan Hedberg14c0b602011-12-15 00:47:37 +02001008
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001009 err = update_class(hdev);
1010
1011 if (err == 0)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001012 err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001013
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001014 hci_dev_unlock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001015 hci_dev_put(hdev);
1016
1017 return err;
1018}
1019
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001020static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001021{
1022 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001023 struct mgmt_cp_load_link_keys *cp = data;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001024 u16 key_count, expected_len;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001025 int i;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001026
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001027 if (len < sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001028 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1029 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001030
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001031 key_count = get_unaligned_le16(&cp->key_count);
1032
Johan Hedberg86742e12011-11-07 23:13:38 +02001033 expected_len = sizeof(*cp) + key_count *
1034 sizeof(struct mgmt_link_key_info);
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001035 if (expected_len != len) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001036 BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001037 len, expected_len);
Johan Hedbergca69b792011-11-11 18:10:00 +02001038 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1039 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001040 }
1041
Szymon Janc4e51eae2011-02-25 19:05:48 +01001042 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001043 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001044 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1045 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001046
Szymon Janc4e51eae2011-02-25 19:05:48 +01001047 BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001048 key_count);
1049
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001050 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001051
1052 hci_link_keys_clear(hdev);
1053
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001054 set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001055
1056 if (cp->debug_keys)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001057 set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001058 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001059 clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001060
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001061 for (i = 0; i < key_count; i++) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001062 struct mgmt_link_key_info *key = &cp->keys[i];
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001063
Johan Hedbergd25e28a2011-04-28 11:28:59 -07001064 hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001065 key->pin_len);
1066 }
1067
Johan Hedberg0e5f8752011-11-11 16:18:54 +02001068 cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
1069
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001070 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001071 hci_dev_put(hdev);
1072
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001073 return 0;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001074}
1075
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001076static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
1077 u8 addr_type, struct sock *skip_sk)
1078{
1079 struct mgmt_ev_device_unpaired ev;
1080
1081 bacpy(&ev.addr.bdaddr, bdaddr);
1082 ev.addr.type = addr_type;
1083
1084 return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev),
1085 skip_sk);
1086}
1087
Johan Hedberg124f6e32012-02-09 13:50:12 +02001088static int unpair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001089{
1090 struct hci_dev *hdev;
Johan Hedberg124f6e32012-02-09 13:50:12 +02001091 struct mgmt_cp_unpair_device *cp = data;
1092 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001093 struct hci_cp_disconnect dc;
1094 struct pending_cmd *cmd;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001095 struct hci_conn *conn;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001096 int err;
1097
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001098 if (len != sizeof(*cp))
Johan Hedberg124f6e32012-02-09 13:50:12 +02001099 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001100 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001101
Szymon Janc4e51eae2011-02-25 19:05:48 +01001102 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001103 if (!hdev)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001104 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001105 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001106
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001107 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001108
Johan Hedberga8a1d192011-11-10 15:54:38 +02001109 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02001110 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1111 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001112
Johan Hedberg124f6e32012-02-09 13:50:12 +02001113 if (cp->addr.type == MGMT_ADDR_BREDR)
1114 err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
1115 else
1116 err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
Vinicius Costa Gomesb0dbfb42012-02-02 21:08:03 -03001117
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001118 if (err < 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001119 rp.status = MGMT_STATUS_NOT_PAIRED;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001120 goto unlock;
1121 }
1122
Johan Hedberga8a1d192011-11-10 15:54:38 +02001123 if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
Johan Hedberg124f6e32012-02-09 13:50:12 +02001124 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001125 sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001126 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001127 goto unlock;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001128 }
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001129
Johan Hedberg124f6e32012-02-09 13:50:12 +02001130 if (cp->addr.type == MGMT_ADDR_BREDR)
1131 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
1132 &cp->addr.bdaddr);
1133 else
1134 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
1135 &cp->addr.bdaddr);
1136
Johan Hedberga8a1d192011-11-10 15:54:38 +02001137 if (!conn) {
Johan Hedberg124f6e32012-02-09 13:50:12 +02001138 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001139 sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001140 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberga8a1d192011-11-10 15:54:38 +02001141 goto unlock;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001142 }
1143
Johan Hedberg124f6e32012-02-09 13:50:12 +02001144 cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
1145 sizeof(*cp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02001146 if (!cmd) {
1147 err = -ENOMEM;
1148 goto unlock;
1149 }
1150
1151 put_unaligned_le16(conn->handle, &dc.handle);
1152 dc.reason = 0x13; /* Remote User Terminated Connection */
1153 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1154 if (err < 0)
1155 mgmt_pending_remove(cmd);
1156
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001157unlock:
Johan Hedbergca69b792011-11-11 18:10:00 +02001158 if (err < 0)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001159 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001160 sizeof(rp));
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001161 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001162 hci_dev_put(hdev);
1163
1164 return err;
1165}
1166
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001167static int disconnect(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg8962ee72011-01-20 12:40:27 +02001168{
1169 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001170 struct mgmt_cp_disconnect *cp = data;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001171 struct hci_cp_disconnect dc;
Johan Hedberg366a0332011-02-19 12:05:55 -03001172 struct pending_cmd *cmd;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001173 struct hci_conn *conn;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001174 int err;
1175
1176 BT_DBG("");
1177
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001178 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001179 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1180 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001181
Szymon Janc4e51eae2011-02-25 19:05:48 +01001182 hdev = hci_dev_get(index);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001183 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001184 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1185 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001186
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001187 hci_dev_lock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001188
1189 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001190 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1191 MGMT_STATUS_NOT_POWERED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001192 goto failed;
1193 }
1194
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001195 if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001196 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1197 MGMT_STATUS_BUSY);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001198 goto failed;
1199 }
1200
Johan Hedberg88c3df12012-02-09 14:27:38 +02001201 if (cp->addr.type == MGMT_ADDR_BREDR)
1202 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
1203 else
1204 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
Vinicius Costa Gomes365227e2011-05-06 18:41:44 -03001205
Johan Hedberg8962ee72011-01-20 12:40:27 +02001206 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001207 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1208 MGMT_STATUS_NOT_CONNECTED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001209 goto failed;
1210 }
1211
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001212 cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001213 if (!cmd) {
1214 err = -ENOMEM;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001215 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001216 }
Johan Hedberg8962ee72011-01-20 12:40:27 +02001217
1218 put_unaligned_le16(conn->handle, &dc.handle);
1219 dc.reason = 0x13; /* Remote User Terminated Connection */
1220
1221 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1222 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001223 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001224
1225failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001226 hci_dev_unlock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001227 hci_dev_put(hdev);
1228
1229 return err;
1230}
1231
Johan Hedberg48264f02011-11-09 13:58:58 +02001232static u8 link_to_mgmt(u8 link_type, u8 addr_type)
Johan Hedberg4c659c32011-11-07 23:13:39 +02001233{
1234 switch (link_type) {
1235 case LE_LINK:
Johan Hedberg48264f02011-11-09 13:58:58 +02001236 switch (addr_type) {
1237 case ADDR_LE_DEV_PUBLIC:
1238 return MGMT_ADDR_LE_PUBLIC;
1239 case ADDR_LE_DEV_RANDOM:
1240 return MGMT_ADDR_LE_RANDOM;
1241 default:
1242 return MGMT_ADDR_INVALID;
1243 }
Johan Hedberg4c659c32011-11-07 23:13:39 +02001244 case ACL_LINK:
1245 return MGMT_ADDR_BREDR;
1246 default:
1247 return MGMT_ADDR_INVALID;
1248 }
1249}
1250
Szymon Janc8ce62842011-03-01 16:55:32 +01001251static int get_connections(struct sock *sk, u16 index)
Johan Hedberg2784eb42011-01-21 13:56:35 +02001252{
Johan Hedberg2784eb42011-01-21 13:56:35 +02001253 struct mgmt_rp_get_connections *rp;
1254 struct hci_dev *hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001255 struct hci_conn *c;
Johan Hedberga38528f2011-01-22 06:46:43 +02001256 size_t rp_len;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001257 u16 count;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001258 int i, err;
1259
1260 BT_DBG("");
1261
Szymon Janc4e51eae2011-02-25 19:05:48 +01001262 hdev = hci_dev_get(index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001263 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001264 return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
1265 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001266
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001267 hci_dev_lock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001268
1269 count = 0;
Johan Hedbergb644ba32012-01-17 21:48:47 +02001270 list_for_each_entry(c, &hdev->conn_hash.list, list) {
1271 if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1272 count++;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001273 }
1274
Johan Hedberg4c659c32011-11-07 23:13:39 +02001275 rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
Johan Hedberga38528f2011-01-22 06:46:43 +02001276 rp = kmalloc(rp_len, GFP_ATOMIC);
1277 if (!rp) {
Johan Hedberg2784eb42011-01-21 13:56:35 +02001278 err = -ENOMEM;
1279 goto unlock;
1280 }
1281
Johan Hedberg2784eb42011-01-21 13:56:35 +02001282 put_unaligned_le16(count, &rp->conn_count);
1283
Johan Hedberg2784eb42011-01-21 13:56:35 +02001284 i = 0;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001285 list_for_each_entry(c, &hdev->conn_hash.list, list) {
Johan Hedbergb644ba32012-01-17 21:48:47 +02001286 if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1287 continue;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001288 bacpy(&rp->addr[i].bdaddr, &c->dst);
Johan Hedberg48264f02011-11-09 13:58:58 +02001289 rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
Johan Hedberg4c659c32011-11-07 23:13:39 +02001290 if (rp->addr[i].type == MGMT_ADDR_INVALID)
1291 continue;
1292 i++;
1293 }
1294
1295 /* Recalculate length in case of filtered SCO connections, etc */
1296 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberg2784eb42011-01-21 13:56:35 +02001297
Szymon Janc4e51eae2011-02-25 19:05:48 +01001298 err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001299
1300unlock:
Johan Hedberga38528f2011-01-22 06:46:43 +02001301 kfree(rp);
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001302 hci_dev_unlock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001303 hci_dev_put(hdev);
1304 return err;
1305}
1306
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001307static int send_pin_code_neg_reply(struct sock *sk, u16 index,
1308 struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
1309{
1310 struct pending_cmd *cmd;
1311 int err;
1312
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001313 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001314 sizeof(*cp));
1315 if (!cmd)
1316 return -ENOMEM;
1317
1318 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
1319 &cp->bdaddr);
1320 if (err < 0)
1321 mgmt_pending_remove(cmd);
1322
1323 return err;
1324}
1325
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001326static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001327{
1328 struct hci_dev *hdev;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001329 struct hci_conn *conn;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001330 struct mgmt_cp_pin_code_reply *cp = data;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001331 struct mgmt_cp_pin_code_neg_reply ncp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001332 struct hci_cp_pin_code_reply reply;
Johan Hedberg366a0332011-02-19 12:05:55 -03001333 struct pending_cmd *cmd;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001334 int err;
1335
1336 BT_DBG("");
1337
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001338 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001339 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1340 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001341
Szymon Janc4e51eae2011-02-25 19:05:48 +01001342 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001343 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001344 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1345 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001346
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001347 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001348
1349 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001350 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1351 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001352 goto failed;
1353 }
1354
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001355 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
1356 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001357 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1358 MGMT_STATUS_NOT_CONNECTED);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001359 goto failed;
1360 }
1361
1362 if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
1363 bacpy(&ncp.bdaddr, &cp->bdaddr);
1364
1365 BT_ERR("PIN code is not 16 bytes long");
1366
1367 err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
1368 if (err >= 0)
1369 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001370 MGMT_STATUS_INVALID_PARAMS);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001371
1372 goto failed;
1373 }
1374
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001375 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data,
1376 len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001377 if (!cmd) {
1378 err = -ENOMEM;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001379 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001380 }
Johan Hedberg980e1a52011-01-22 06:10:07 +02001381
1382 bacpy(&reply.bdaddr, &cp->bdaddr);
1383 reply.pin_len = cp->pin_len;
Waldemar Rymarkiewicz24718ca2011-06-01 17:28:47 +02001384 memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
Johan Hedberg980e1a52011-01-22 06:10:07 +02001385
1386 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
1387 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001388 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001389
1390failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001391 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001392 hci_dev_put(hdev);
1393
1394 return err;
1395}
1396
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001397static int pin_code_neg_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001398{
1399 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001400 struct mgmt_cp_pin_code_neg_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001401 int err;
1402
1403 BT_DBG("");
1404
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001405 if (len != sizeof(*cp))
1406 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001407 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001408
Szymon Janc4e51eae2011-02-25 19:05:48 +01001409 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001410 if (!hdev)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001411 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001412 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001413
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001414 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001415
1416 if (!test_bit(HCI_UP, &hdev->flags)) {
Szymon Janc4e51eae2011-02-25 19:05:48 +01001417 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001418 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001419 goto failed;
1420 }
1421
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001422 err = send_pin_code_neg_reply(sk, index, hdev, cp);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001423
1424failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001425 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001426 hci_dev_put(hdev);
1427
1428 return err;
1429}
1430
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001431static int set_io_capability(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001432{
1433 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001434 struct mgmt_cp_set_io_capability *cp = data;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001435
1436 BT_DBG("");
1437
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001438 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001439 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1440 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001441
Szymon Janc4e51eae2011-02-25 19:05:48 +01001442 hdev = hci_dev_get(index);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001443 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001444 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1445 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001446
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001447 hci_dev_lock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001448
1449 hdev->io_capability = cp->io_capability;
1450
1451 BT_DBG("%s IO capability set to 0x%02x", hdev->name,
Szymon Jancb8534e02011-03-01 16:55:34 +01001452 hdev->io_capability);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001453
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001454 hci_dev_unlock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001455 hci_dev_put(hdev);
1456
Szymon Janc4e51eae2011-02-25 19:05:48 +01001457 return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001458}
1459
Johan Hedberge9a416b2011-02-19 12:05:56 -03001460static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1461{
1462 struct hci_dev *hdev = conn->hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001463 struct pending_cmd *cmd;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001464
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001465 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
Johan Hedberge9a416b2011-02-19 12:05:56 -03001466 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1467 continue;
1468
Johan Hedberge9a416b2011-02-19 12:05:56 -03001469 if (cmd->user_data != conn)
1470 continue;
1471
1472 return cmd;
1473 }
1474
1475 return NULL;
1476}
1477
1478static void pairing_complete(struct pending_cmd *cmd, u8 status)
1479{
1480 struct mgmt_rp_pair_device rp;
1481 struct hci_conn *conn = cmd->user_data;
1482
Johan Hedbergba4e5642011-11-11 00:07:34 +02001483 bacpy(&rp.addr.bdaddr, &conn->dst);
1484 rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001485 rp.status = status;
1486
Szymon Janc4e51eae2011-02-25 19:05:48 +01001487 cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001488
1489 /* So we don't get further callbacks for this connection */
1490 conn->connect_cfm_cb = NULL;
1491 conn->security_cfm_cb = NULL;
1492 conn->disconn_cfm_cb = NULL;
1493
1494 hci_conn_put(conn);
1495
Johan Hedberga664b5b2011-02-19 12:06:02 -03001496 mgmt_pending_remove(cmd);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001497}
1498
1499static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1500{
1501 struct pending_cmd *cmd;
1502
1503 BT_DBG("status %u", status);
1504
Johan Hedberg56e5cb82011-11-08 20:40:16 +02001505 cmd = find_pairing(conn);
1506 if (!cmd)
1507 BT_DBG("Unable to find a pending command");
1508 else
1509 pairing_complete(cmd, status);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001510}
1511
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001512static int pair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001513{
1514 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001515 struct mgmt_cp_pair_device *cp = data;
Johan Hedberg1425acb2011-11-11 00:07:35 +02001516 struct mgmt_rp_pair_device rp;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001517 struct pending_cmd *cmd;
1518 u8 sec_level, auth_type;
1519 struct hci_conn *conn;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001520 int err;
1521
1522 BT_DBG("");
1523
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001524 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001525 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1526 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001527
Szymon Janc4e51eae2011-02-25 19:05:48 +01001528 hdev = hci_dev_get(index);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001529 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001530 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1531 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001532
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001533 hci_dev_lock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001534
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001535 sec_level = BT_SECURITY_MEDIUM;
1536 if (cp->io_cap == 0x03)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001537 auth_type = HCI_AT_DEDICATED_BONDING;
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001538 else
Johan Hedberge9a416b2011-02-19 12:05:56 -03001539 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001540
Johan Hedbergba4e5642011-11-11 00:07:34 +02001541 if (cp->addr.type == MGMT_ADDR_BREDR)
1542 conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001543 auth_type);
1544 else
Johan Hedbergba4e5642011-11-11 00:07:34 +02001545 conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001546 auth_type);
1547
Johan Hedberg1425acb2011-11-11 00:07:35 +02001548 memset(&rp, 0, sizeof(rp));
1549 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1550 rp.addr.type = cp->addr.type;
1551
Ville Tervo30e76272011-02-22 16:10:53 -03001552 if (IS_ERR(conn)) {
Johan Hedberg1425acb2011-11-11 00:07:35 +02001553 rp.status = -PTR_ERR(conn);
1554 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1555 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001556 goto unlock;
1557 }
1558
1559 if (conn->connect_cfm_cb) {
1560 hci_conn_put(conn);
Johan Hedberg1425acb2011-11-11 00:07:35 +02001561 rp.status = EBUSY;
1562 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1563 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001564 goto unlock;
1565 }
1566
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001567 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001568 if (!cmd) {
1569 err = -ENOMEM;
1570 hci_conn_put(conn);
1571 goto unlock;
1572 }
1573
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001574 /* For LE, just connecting isn't a proof that the pairing finished */
Johan Hedbergba4e5642011-11-11 00:07:34 +02001575 if (cp->addr.type == MGMT_ADDR_BREDR)
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001576 conn->connect_cfm_cb = pairing_complete_cb;
1577
Johan Hedberge9a416b2011-02-19 12:05:56 -03001578 conn->security_cfm_cb = pairing_complete_cb;
1579 conn->disconn_cfm_cb = pairing_complete_cb;
1580 conn->io_capability = cp->io_cap;
1581 cmd->user_data = conn;
1582
1583 if (conn->state == BT_CONNECTED &&
1584 hci_conn_security(conn, sec_level, auth_type))
1585 pairing_complete(cmd, 0);
1586
1587 err = 0;
1588
1589unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001590 hci_dev_unlock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001591 hci_dev_put(hdev);
1592
1593 return err;
1594}
1595
Johan Hedberg28424702012-02-02 04:02:29 +02001596static int cancel_pair_device(struct sock *sk, u16 index,
1597 unsigned char *data, u16 len)
1598{
1599 struct mgmt_addr_info *addr = (void *) data;
1600 struct hci_dev *hdev;
1601 struct pending_cmd *cmd;
1602 struct hci_conn *conn;
1603 int err;
1604
1605 BT_DBG("");
1606
1607 if (len != sizeof(*addr))
1608 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1609 MGMT_STATUS_INVALID_PARAMS);
1610
1611 hdev = hci_dev_get(index);
1612 if (!hdev)
1613 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1614 MGMT_STATUS_INVALID_PARAMS);
1615
1616 hci_dev_lock(hdev);
1617
1618 cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
1619 if (!cmd) {
1620 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1621 MGMT_STATUS_INVALID_PARAMS);
1622 goto unlock;
1623 }
1624
1625 conn = cmd->user_data;
1626
1627 if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
1628 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1629 MGMT_STATUS_INVALID_PARAMS);
1630 goto unlock;
1631 }
1632
1633 pairing_complete(cmd, MGMT_STATUS_CANCELLED);
1634
1635 err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, addr,
1636 sizeof(*addr));
1637unlock:
1638 hci_dev_unlock(hdev);
1639 hci_dev_put(hdev);
1640
1641 return err;
1642}
1643
Brian Gix0df4c182011-11-16 13:53:13 -08001644static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02001645 u8 type, u16 mgmt_op, u16 hci_op,
1646 __le32 passkey)
Johan Hedberga5c29682011-02-19 12:05:57 -03001647{
Johan Hedberga5c29682011-02-19 12:05:57 -03001648 struct pending_cmd *cmd;
1649 struct hci_dev *hdev;
Brian Gix0df4c182011-11-16 13:53:13 -08001650 struct hci_conn *conn;
Johan Hedberga5c29682011-02-19 12:05:57 -03001651 int err;
1652
Szymon Janc4e51eae2011-02-25 19:05:48 +01001653 hdev = hci_dev_get(index);
Johan Hedberga5c29682011-02-19 12:05:57 -03001654 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001655 return cmd_status(sk, index, mgmt_op,
1656 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberga5c29682011-02-19 12:05:57 -03001657
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001658 hci_dev_lock(hdev);
Johan Hedberg08ba5382011-03-16 14:29:34 +02001659
Johan Hedberga5c29682011-02-19 12:05:57 -03001660 if (!test_bit(HCI_UP, &hdev->flags)) {
Brian Gix0df4c182011-11-16 13:53:13 -08001661 err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
1662 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001663 }
1664
Johan Hedberg272d90d2012-02-09 15:26:12 +02001665 if (type == MGMT_ADDR_BREDR)
1666 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
1667 else
Brian Gix47c15e22011-11-16 13:53:14 -08001668 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
Brian Gix47c15e22011-11-16 13:53:14 -08001669
Johan Hedberg272d90d2012-02-09 15:26:12 +02001670 if (!conn) {
1671 err = cmd_status(sk, index, mgmt_op,
1672 MGMT_STATUS_NOT_CONNECTED);
1673 goto done;
1674 }
1675
1676 if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
Brian Gix47c15e22011-11-16 13:53:14 -08001677 /* Continue with pairing via SMP */
Brian Gix5fe57d92011-12-21 16:12:13 -08001678 err = smp_user_confirm_reply(conn, mgmt_op, passkey);
Brian Gix47c15e22011-11-16 13:53:14 -08001679
Brian Gix5fe57d92011-12-21 16:12:13 -08001680 if (!err)
1681 err = cmd_status(sk, index, mgmt_op,
1682 MGMT_STATUS_SUCCESS);
1683 else
1684 err = cmd_status(sk, index, mgmt_op,
1685 MGMT_STATUS_FAILED);
1686
Brian Gix47c15e22011-11-16 13:53:14 -08001687 goto done;
1688 }
1689
Brian Gix0df4c182011-11-16 13:53:13 -08001690 cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
Johan Hedberga5c29682011-02-19 12:05:57 -03001691 if (!cmd) {
1692 err = -ENOMEM;
Brian Gix0df4c182011-11-16 13:53:13 -08001693 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001694 }
1695
Brian Gix0df4c182011-11-16 13:53:13 -08001696 /* Continue with pairing via HCI */
Brian Gix604086b2011-11-23 08:28:33 -08001697 if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
1698 struct hci_cp_user_passkey_reply cp;
1699
1700 bacpy(&cp.bdaddr, bdaddr);
1701 cp.passkey = passkey;
1702 err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
1703 } else
1704 err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
1705
Johan Hedberga664b5b2011-02-19 12:06:02 -03001706 if (err < 0)
1707 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03001708
Brian Gix0df4c182011-11-16 13:53:13 -08001709done:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001710 hci_dev_unlock(hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03001711 hci_dev_put(hdev);
1712
1713 return err;
1714}
1715
Brian Gix0df4c182011-11-16 13:53:13 -08001716static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
1717{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001718 struct mgmt_cp_user_confirm_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001719
1720 BT_DBG("");
1721
1722 if (len != sizeof(*cp))
1723 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
1724 MGMT_STATUS_INVALID_PARAMS);
1725
Johan Hedberg272d90d2012-02-09 15:26:12 +02001726 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1727 MGMT_OP_USER_CONFIRM_REPLY,
1728 HCI_OP_USER_CONFIRM_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001729}
1730
1731static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
1732 u16 len)
1733{
Johan Hedbergc9c26592011-12-15 00:47:41 +02001734 struct mgmt_cp_user_confirm_neg_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001735
1736 BT_DBG("");
1737
1738 if (len != sizeof(*cp))
1739 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
1740 MGMT_STATUS_INVALID_PARAMS);
1741
Johan Hedberg272d90d2012-02-09 15:26:12 +02001742 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1743 MGMT_OP_USER_CONFIRM_NEG_REPLY,
1744 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001745}
1746
Brian Gix604086b2011-11-23 08:28:33 -08001747static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
1748{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001749 struct mgmt_cp_user_passkey_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001750
1751 BT_DBG("");
1752
1753 if (len != sizeof(*cp))
1754 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
1755 EINVAL);
1756
Johan Hedberg272d90d2012-02-09 15:26:12 +02001757 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1758 MGMT_OP_USER_PASSKEY_REPLY,
1759 HCI_OP_USER_PASSKEY_REPLY,
1760 cp->passkey);
Brian Gix604086b2011-11-23 08:28:33 -08001761}
1762
1763static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
1764 u16 len)
1765{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001766 struct mgmt_cp_user_passkey_neg_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001767
1768 BT_DBG("");
1769
1770 if (len != sizeof(*cp))
1771 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
1772 EINVAL);
1773
Johan Hedberg272d90d2012-02-09 15:26:12 +02001774 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1775 MGMT_OP_USER_PASSKEY_NEG_REPLY,
1776 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
Brian Gix604086b2011-11-23 08:28:33 -08001777}
1778
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001779static int set_local_name(struct sock *sk, u16 index, void *data,
Johan Hedbergb312b1612011-03-16 14:29:37 +02001780 u16 len)
1781{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001782 struct mgmt_cp_set_local_name *mgmt_cp = data;
Johan Hedbergb312b1612011-03-16 14:29:37 +02001783 struct hci_cp_write_local_name hci_cp;
1784 struct hci_dev *hdev;
1785 struct pending_cmd *cmd;
1786 int err;
1787
1788 BT_DBG("");
1789
1790 if (len != sizeof(*mgmt_cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001791 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
1792 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001793
1794 hdev = hci_dev_get(index);
1795 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001796 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
1797 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001798
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001799 hci_dev_lock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001800
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001801 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data,
1802 len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001803 if (!cmd) {
1804 err = -ENOMEM;
1805 goto failed;
1806 }
1807
1808 memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
1809 err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
1810 &hci_cp);
1811 if (err < 0)
1812 mgmt_pending_remove(cmd);
1813
1814failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001815 hci_dev_unlock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001816 hci_dev_put(hdev);
1817
1818 return err;
1819}
1820
Szymon Jancc35938b2011-03-22 13:12:21 +01001821static int read_local_oob_data(struct sock *sk, u16 index)
1822{
1823 struct hci_dev *hdev;
1824 struct pending_cmd *cmd;
1825 int err;
1826
1827 BT_DBG("hci%u", index);
1828
1829 hdev = hci_dev_get(index);
1830 if (!hdev)
1831 return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001832 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancc35938b2011-03-22 13:12:21 +01001833
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001834 hci_dev_lock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01001835
1836 if (!test_bit(HCI_UP, &hdev->flags)) {
1837 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001838 MGMT_STATUS_NOT_POWERED);
Szymon Jancc35938b2011-03-22 13:12:21 +01001839 goto unlock;
1840 }
1841
1842 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
1843 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001844 MGMT_STATUS_NOT_SUPPORTED);
Szymon Jancc35938b2011-03-22 13:12:21 +01001845 goto unlock;
1846 }
1847
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001848 if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001849 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
1850 MGMT_STATUS_BUSY);
Szymon Jancc35938b2011-03-22 13:12:21 +01001851 goto unlock;
1852 }
1853
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001854 cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
Szymon Jancc35938b2011-03-22 13:12:21 +01001855 if (!cmd) {
1856 err = -ENOMEM;
1857 goto unlock;
1858 }
1859
1860 err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
1861 if (err < 0)
1862 mgmt_pending_remove(cmd);
1863
1864unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001865 hci_dev_unlock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01001866 hci_dev_put(hdev);
1867
1868 return err;
1869}
1870
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001871static int add_remote_oob_data(struct sock *sk, u16 index, void *data,
1872 u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01001873{
1874 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001875 struct mgmt_cp_add_remote_oob_data *cp = data;
Szymon Janc2763eda2011-03-22 13:12:22 +01001876 int err;
1877
1878 BT_DBG("hci%u ", index);
1879
1880 if (len != sizeof(*cp))
1881 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001882 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001883
1884 hdev = hci_dev_get(index);
1885 if (!hdev)
1886 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001887 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001888
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001889 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001890
Johan Hedberg664ce4c2012-02-09 15:44:09 +02001891 err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
Szymon Janc2763eda2011-03-22 13:12:22 +01001892 cp->randomizer);
1893 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02001894 err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
1895 MGMT_STATUS_FAILED);
Szymon Janc2763eda2011-03-22 13:12:22 +01001896 else
1897 err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
1898 0);
1899
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001900 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001901 hci_dev_put(hdev);
1902
1903 return err;
1904}
1905
1906static int remove_remote_oob_data(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001907 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01001908{
1909 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001910 struct mgmt_cp_remove_remote_oob_data *cp = data;
Szymon Janc2763eda2011-03-22 13:12:22 +01001911 int err;
1912
1913 BT_DBG("hci%u ", index);
1914
1915 if (len != sizeof(*cp))
1916 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001917 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001918
1919 hdev = hci_dev_get(index);
1920 if (!hdev)
1921 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001922 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001923
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001924 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001925
Johan Hedberg664ce4c2012-02-09 15:44:09 +02001926 err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
Szymon Janc2763eda2011-03-22 13:12:22 +01001927 if (err < 0)
1928 err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001929 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001930 else
1931 err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
1932 NULL, 0);
1933
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001934 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001935 hci_dev_put(hdev);
1936
1937 return err;
1938}
1939
Johan Hedberg450dfda2011-11-12 11:58:22 +02001940static int start_discovery(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001941 void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04001942{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001943 struct mgmt_cp_start_discovery *cp = data;
Andre Guedes3fd24152012-02-03 17:48:01 -03001944 unsigned long discov_type = cp->type;
Johan Hedberg14a53662011-04-27 10:29:56 -04001945 struct pending_cmd *cmd;
1946 struct hci_dev *hdev;
1947 int err;
1948
1949 BT_DBG("hci%u", index);
1950
Johan Hedberg450dfda2011-11-12 11:58:22 +02001951 if (len != sizeof(*cp))
1952 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1953 MGMT_STATUS_INVALID_PARAMS);
1954
Johan Hedberg14a53662011-04-27 10:29:56 -04001955 hdev = hci_dev_get(index);
1956 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001957 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1958 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04001959
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001960 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04001961
Johan Hedbergbd2d1332011-11-07 23:13:37 +02001962 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001963 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1964 MGMT_STATUS_NOT_POWERED);
Johan Hedbergbd2d1332011-11-07 23:13:37 +02001965 goto failed;
1966 }
1967
Johan Hedbergff9ef572012-01-04 14:23:45 +02001968 if (hdev->discovery.state != DISCOVERY_STOPPED) {
1969 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1970 MGMT_STATUS_BUSY);
1971 goto failed;
1972 }
1973
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001974 cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04001975 if (!cmd) {
1976 err = -ENOMEM;
1977 goto failed;
1978 }
1979
Andre Guedes3fd24152012-02-03 17:48:01 -03001980 if (test_bit(MGMT_ADDR_BREDR, &discov_type))
1981 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
1982 else if (test_bit(MGMT_ADDR_LE_PUBLIC, &discov_type) &&
1983 test_bit(MGMT_ADDR_LE_RANDOM, &discov_type))
1984 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
1985 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
1986 else
1987 err = -EINVAL;
1988
Johan Hedberg14a53662011-04-27 10:29:56 -04001989 if (err < 0)
1990 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02001991 else
1992 hci_discovery_set_state(hdev, DISCOVERY_STARTING);
Johan Hedberg14a53662011-04-27 10:29:56 -04001993
1994failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001995 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04001996 hci_dev_put(hdev);
1997
1998 return err;
1999}
2000
2001static int stop_discovery(struct sock *sk, u16 index)
2002{
2003 struct hci_dev *hdev;
2004 struct pending_cmd *cmd;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002005 struct hci_cp_remote_name_req_cancel cp;
2006 struct inquiry_entry *e;
Johan Hedberg14a53662011-04-27 10:29:56 -04002007 int err;
2008
2009 BT_DBG("hci%u", index);
2010
2011 hdev = hci_dev_get(index);
2012 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002013 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2014 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002015
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002016 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002017
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002018 if (!hci_discovery_active(hdev)) {
Johan Hedbergff9ef572012-01-04 14:23:45 +02002019 err = cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2020 MGMT_STATUS_REJECTED);
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002021 goto unlock;
Johan Hedbergff9ef572012-01-04 14:23:45 +02002022 }
2023
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002024 cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002025 if (!cmd) {
2026 err = -ENOMEM;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002027 goto unlock;
Johan Hedberg14a53662011-04-27 10:29:56 -04002028 }
2029
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002030 if (hdev->discovery.state == DISCOVERY_INQUIRY) {
2031 err = hci_cancel_inquiry(hdev);
2032 if (err < 0)
2033 mgmt_pending_remove(cmd);
2034 else
2035 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
2036 goto unlock;
2037 }
2038
2039 e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
2040 if (!e) {
2041 mgmt_pending_remove(cmd);
2042 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, NULL, 0);
2043 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2044 goto unlock;
2045 }
2046
2047 bacpy(&cp.bdaddr, &e->data.bdaddr);
2048 err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
2049 sizeof(cp), &cp);
Johan Hedberg14a53662011-04-27 10:29:56 -04002050 if (err < 0)
2051 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002052 else
2053 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002054
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002055unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002056 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002057 hci_dev_put(hdev);
2058
2059 return err;
2060}
2061
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002062static int confirm_name(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg561aafb2012-01-04 13:31:59 +02002063{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002064 struct mgmt_cp_confirm_name *cp = data;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002065 struct inquiry_entry *e;
2066 struct hci_dev *hdev;
2067 int err;
2068
2069 BT_DBG("hci%u", index);
2070
2071 if (len != sizeof(*cp))
2072 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2073 MGMT_STATUS_INVALID_PARAMS);
2074
2075 hdev = hci_dev_get(index);
2076 if (!hdev)
2077 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2078 MGMT_STATUS_INVALID_PARAMS);
2079
2080 hci_dev_lock(hdev);
2081
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002082 if (!hci_discovery_active(hdev)) {
2083 err = cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2084 MGMT_STATUS_FAILED);
2085 goto failed;
2086 }
2087
Johan Hedberg561aafb2012-01-04 13:31:59 +02002088 e = hci_inquiry_cache_lookup_unknown(hdev, &cp->bdaddr);
2089 if (!e) {
2090 err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME,
2091 MGMT_STATUS_INVALID_PARAMS);
2092 goto failed;
2093 }
2094
2095 if (cp->name_known) {
2096 e->name_state = NAME_KNOWN;
2097 list_del(&e->list);
2098 } else {
2099 e->name_state = NAME_NEEDED;
Johan Hedberga3d4e202012-01-09 00:53:02 +02002100 hci_inquiry_cache_update_resolve(hdev, e);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002101 }
2102
2103 err = 0;
2104
2105failed:
2106 hci_dev_unlock(hdev);
2107
2108 return err;
2109}
2110
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002111static int block_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002112{
2113 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002114 struct mgmt_cp_block_device *cp = data;
Antti Julku7fbec222011-06-15 12:01:15 +03002115 int err;
2116
2117 BT_DBG("hci%u", index);
2118
Antti Julku7fbec222011-06-15 12:01:15 +03002119 if (len != sizeof(*cp))
2120 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002121 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002122
2123 hdev = hci_dev_get(index);
2124 if (!hdev)
2125 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002126 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002127
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002128 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002129
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002130 err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002131 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02002132 err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
2133 MGMT_STATUS_FAILED);
Antti Julku7fbec222011-06-15 12:01:15 +03002134 else
2135 err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
2136 NULL, 0);
Antti Julku5e762442011-08-25 16:48:02 +03002137
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002138 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002139 hci_dev_put(hdev);
2140
2141 return err;
2142}
2143
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002144static int unblock_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002145{
2146 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002147 struct mgmt_cp_unblock_device *cp = data;
Antti Julku7fbec222011-06-15 12:01:15 +03002148 int err;
2149
2150 BT_DBG("hci%u", index);
2151
Antti Julku7fbec222011-06-15 12:01:15 +03002152 if (len != sizeof(*cp))
2153 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002154 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002155
2156 hdev = hci_dev_get(index);
2157 if (!hdev)
2158 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002159 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002160
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002161 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002162
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002163 err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002164
2165 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02002166 err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2167 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002168 else
2169 err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2170 NULL, 0);
Antti Julku5e762442011-08-25 16:48:02 +03002171
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002172 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002173 hci_dev_put(hdev);
2174
2175 return err;
2176}
2177
Antti Julkuf6422ec2011-06-22 13:11:56 +03002178static int set_fast_connectable(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002179 void *data, u16 len)
Antti Julkuf6422ec2011-06-22 13:11:56 +03002180{
2181 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002182 struct mgmt_mode *cp = data;
Antti Julkuf6422ec2011-06-22 13:11:56 +03002183 struct hci_cp_write_page_scan_activity acp;
2184 u8 type;
2185 int err;
2186
2187 BT_DBG("hci%u", index);
2188
2189 if (len != sizeof(*cp))
2190 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002191 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002192
2193 hdev = hci_dev_get(index);
2194 if (!hdev)
2195 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002196 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002197
2198 hci_dev_lock(hdev);
2199
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002200 if (cp->val) {
Antti Julkuf6422ec2011-06-22 13:11:56 +03002201 type = PAGE_SCAN_TYPE_INTERLACED;
2202 acp.interval = 0x0024; /* 22.5 msec page scan interval */
2203 } else {
2204 type = PAGE_SCAN_TYPE_STANDARD; /* default */
2205 acp.interval = 0x0800; /* default 1.28 sec page scan */
2206 }
2207
2208 acp.window = 0x0012; /* default 11.25 msec page scan window */
2209
2210 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
2211 sizeof(acp), &acp);
2212 if (err < 0) {
2213 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002214 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002215 goto done;
2216 }
2217
2218 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
2219 if (err < 0) {
2220 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002221 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002222 goto done;
2223 }
2224
2225 err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
2226 NULL, 0);
2227done:
2228 hci_dev_unlock(hdev);
2229 hci_dev_put(hdev);
2230
2231 return err;
2232}
2233
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002234static int load_long_term_keys(struct sock *sk, u16 index,
2235 void *cp_data, u16 len)
2236{
2237 struct hci_dev *hdev;
2238 struct mgmt_cp_load_long_term_keys *cp = cp_data;
2239 u16 key_count, expected_len;
2240 int i;
2241
2242 if (len < sizeof(*cp))
2243 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2244 EINVAL);
2245
2246 key_count = get_unaligned_le16(&cp->key_count);
2247
2248 expected_len = sizeof(*cp) + key_count *
2249 sizeof(struct mgmt_ltk_info);
2250 if (expected_len != len) {
2251 BT_ERR("load_keys: expected %u bytes, got %u bytes",
2252 len, expected_len);
2253 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2254 EINVAL);
2255 }
2256
2257 hdev = hci_dev_get(index);
2258 if (!hdev)
2259 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2260 ENODEV);
2261
2262 BT_DBG("hci%u key_count %u", index, key_count);
2263
2264 hci_dev_lock(hdev);
2265
2266 hci_smp_ltks_clear(hdev);
2267
2268 for (i = 0; i < key_count; i++) {
2269 struct mgmt_ltk_info *key = &cp->keys[i];
2270 u8 type;
2271
2272 if (key->master)
2273 type = HCI_SMP_LTK;
2274 else
2275 type = HCI_SMP_LTK_SLAVE;
2276
2277 hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
2278 type, 0, key->authenticated, key->val,
2279 key->enc_size, key->ediv, key->rand);
2280 }
2281
2282 hci_dev_unlock(hdev);
2283 hci_dev_put(hdev);
2284
2285 return 0;
2286}
2287
Johan Hedberg03811012010-12-08 00:21:06 +02002288int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
2289{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002290 void *buf;
2291 u8 *cp;
Johan Hedberg03811012010-12-08 00:21:06 +02002292 struct mgmt_hdr *hdr;
Szymon Janc4e51eae2011-02-25 19:05:48 +01002293 u16 opcode, index, len;
Johan Hedberg03811012010-12-08 00:21:06 +02002294 int err;
2295
2296 BT_DBG("got %zu bytes", msglen);
2297
2298 if (msglen < sizeof(*hdr))
2299 return -EINVAL;
2300
Gustavo F. Padovane63a15e2011-04-04 18:56:53 -03002301 buf = kmalloc(msglen, GFP_KERNEL);
Johan Hedberg03811012010-12-08 00:21:06 +02002302 if (!buf)
2303 return -ENOMEM;
2304
2305 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
2306 err = -EFAULT;
2307 goto done;
2308 }
2309
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002310 hdr = buf;
Johan Hedberg03811012010-12-08 00:21:06 +02002311 opcode = get_unaligned_le16(&hdr->opcode);
Szymon Janc4e51eae2011-02-25 19:05:48 +01002312 index = get_unaligned_le16(&hdr->index);
Johan Hedberg03811012010-12-08 00:21:06 +02002313 len = get_unaligned_le16(&hdr->len);
2314
2315 if (len != msglen - sizeof(*hdr)) {
2316 err = -EINVAL;
2317 goto done;
2318 }
2319
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002320 cp = buf + sizeof(*hdr);
2321
Johan Hedberg03811012010-12-08 00:21:06 +02002322 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +02002323 case MGMT_OP_READ_VERSION:
2324 err = read_version(sk);
2325 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +02002326 case MGMT_OP_READ_INDEX_LIST:
2327 err = read_index_list(sk);
2328 break;
Johan Hedbergf7b64e692010-12-13 21:07:06 +02002329 case MGMT_OP_READ_INFO:
Szymon Janc4e51eae2011-02-25 19:05:48 +01002330 err = read_controller_info(sk, index);
Johan Hedbergf7b64e692010-12-13 21:07:06 +02002331 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002332 case MGMT_OP_SET_POWERED:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002333 err = set_powered(sk, index, cp, len);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002334 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +02002335 case MGMT_OP_SET_DISCOVERABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002336 err = set_discoverable(sk, index, cp, len);
Johan Hedberg73f22f62010-12-29 16:00:25 +02002337 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002338 case MGMT_OP_SET_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002339 err = set_connectable(sk, index, cp, len);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002340 break;
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002341 case MGMT_OP_SET_FAST_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002342 err = set_fast_connectable(sk, index, cp, len);
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002343 break;
Johan Hedbergc542a062011-01-26 13:11:03 +02002344 case MGMT_OP_SET_PAIRABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002345 err = set_pairable(sk, index, cp, len);
Johan Hedbergc542a062011-01-26 13:11:03 +02002346 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002347 case MGMT_OP_ADD_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002348 err = add_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002349 break;
2350 case MGMT_OP_REMOVE_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002351 err = remove_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002352 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002353 case MGMT_OP_SET_DEV_CLASS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002354 err = set_dev_class(sk, index, cp, len);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002355 break;
Johan Hedberg86742e12011-11-07 23:13:38 +02002356 case MGMT_OP_LOAD_LINK_KEYS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002357 err = load_link_keys(sk, index, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002358 break;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002359 case MGMT_OP_DISCONNECT:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002360 err = disconnect(sk, index, cp, len);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002361 break;
Johan Hedberg2784eb42011-01-21 13:56:35 +02002362 case MGMT_OP_GET_CONNECTIONS:
Szymon Janc8ce62842011-03-01 16:55:32 +01002363 err = get_connections(sk, index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02002364 break;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002365 case MGMT_OP_PIN_CODE_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002366 err = pin_code_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002367 break;
2368 case MGMT_OP_PIN_CODE_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002369 err = pin_code_neg_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002370 break;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002371 case MGMT_OP_SET_IO_CAPABILITY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002372 err = set_io_capability(sk, index, cp, len);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002373 break;
Johan Hedberge9a416b2011-02-19 12:05:56 -03002374 case MGMT_OP_PAIR_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002375 err = pair_device(sk, index, cp, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002376 break;
Johan Hedberg28424702012-02-02 04:02:29 +02002377 case MGMT_OP_CANCEL_PAIR_DEVICE:
2378 err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len);
2379 break;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002380 case MGMT_OP_UNPAIR_DEVICE:
2381 err = unpair_device(sk, index, cp, len);
2382 break;
Johan Hedberga5c29682011-02-19 12:05:57 -03002383 case MGMT_OP_USER_CONFIRM_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002384 err = user_confirm_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002385 break;
2386 case MGMT_OP_USER_CONFIRM_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002387 err = user_confirm_neg_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002388 break;
Brian Gix604086b2011-11-23 08:28:33 -08002389 case MGMT_OP_USER_PASSKEY_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002390 err = user_passkey_reply(sk, index, cp, len);
Brian Gix604086b2011-11-23 08:28:33 -08002391 break;
2392 case MGMT_OP_USER_PASSKEY_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002393 err = user_passkey_neg_reply(sk, index, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002394 break;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002395 case MGMT_OP_SET_LOCAL_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002396 err = set_local_name(sk, index, cp, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002397 break;
Szymon Jancc35938b2011-03-22 13:12:21 +01002398 case MGMT_OP_READ_LOCAL_OOB_DATA:
2399 err = read_local_oob_data(sk, index);
2400 break;
Szymon Janc2763eda2011-03-22 13:12:22 +01002401 case MGMT_OP_ADD_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002402 err = add_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002403 break;
2404 case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002405 err = remove_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002406 break;
Johan Hedberg14a53662011-04-27 10:29:56 -04002407 case MGMT_OP_START_DISCOVERY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002408 err = start_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002409 break;
2410 case MGMT_OP_STOP_DISCOVERY:
2411 err = stop_discovery(sk, index);
2412 break;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002413 case MGMT_OP_CONFIRM_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002414 err = confirm_name(sk, index, cp, len);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002415 break;
Antti Julku7fbec222011-06-15 12:01:15 +03002416 case MGMT_OP_BLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002417 err = block_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002418 break;
2419 case MGMT_OP_UNBLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002420 err = unblock_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002421 break;
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002422 case MGMT_OP_LOAD_LONG_TERM_KEYS:
2423 err = load_long_term_keys(sk, index, cp, len);
2424 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002425 default:
Johan Hedberg72a734e2010-12-30 00:38:22 +02002426 BT_DBG("Unknown op %u", opcode);
Johan Hedbergca69b792011-11-11 18:10:00 +02002427 err = cmd_status(sk, index, opcode,
2428 MGMT_STATUS_UNKNOWN_COMMAND);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002429 break;
2430 }
Johan Hedberg72a734e2010-12-30 00:38:22 +02002431
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002432 if (err < 0)
Johan Hedberg72a734e2010-12-30 00:38:22 +02002433 goto done;
2434
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002435 err = msglen;
2436
2437done:
2438 kfree(buf);
2439 return err;
2440}
2441
Johan Hedbergb24752f2011-11-03 14:40:33 +02002442static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
2443{
2444 u8 *status = data;
2445
2446 cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
2447 mgmt_pending_remove(cmd);
2448}
2449
Johan Hedberg744cf192011-11-08 20:40:14 +02002450int mgmt_index_added(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002451{
Johan Hedberg744cf192011-11-08 20:40:14 +02002452 return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002453}
2454
Johan Hedberg744cf192011-11-08 20:40:14 +02002455int mgmt_index_removed(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002456{
Johan Hedbergb24752f2011-11-03 14:40:33 +02002457 u8 status = ENODEV;
2458
Johan Hedberg744cf192011-11-08 20:40:14 +02002459 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002460
Johan Hedberg744cf192011-11-08 20:40:14 +02002461 return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002462}
2463
2464struct cmd_lookup {
2465 u8 val;
2466 struct sock *sk;
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002467 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +02002468};
2469
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002470static void settings_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberg03811012010-12-08 00:21:06 +02002471{
Johan Hedberg03811012010-12-08 00:21:06 +02002472 struct cmd_lookup *match = data;
2473
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002474 send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
Johan Hedberg03811012010-12-08 00:21:06 +02002475
2476 list_del(&cmd->list);
2477
2478 if (match->sk == NULL) {
2479 match->sk = cmd->sk;
2480 sock_hold(match->sk);
2481 }
2482
2483 mgmt_pending_free(cmd);
2484}
2485
Johan Hedberg744cf192011-11-08 20:40:14 +02002486int mgmt_powered(struct hci_dev *hdev, u8 powered)
Johan Hedberg03811012010-12-08 00:21:06 +02002487{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002488 struct cmd_lookup match = { powered, NULL, hdev };
2489 __le32 ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002490 int ret;
2491
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002492 mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002493
Johan Hedbergb24752f2011-11-03 14:40:33 +02002494 if (!powered) {
2495 u8 status = ENETDOWN;
Johan Hedberg744cf192011-11-08 20:40:14 +02002496 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002497 }
2498
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002499 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002500
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002501 ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
2502 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002503
2504 if (match.sk)
2505 sock_put(match.sk);
2506
2507 return ret;
2508}
2509
Johan Hedberg744cf192011-11-08 20:40:14 +02002510int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
Johan Hedberg03811012010-12-08 00:21:06 +02002511{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002512 struct cmd_lookup match = { discoverable, NULL, hdev };
2513 __le32 ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002514 int ret;
2515
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002516 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002517
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002518 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002519
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002520 ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002521 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002522 if (match.sk)
2523 sock_put(match.sk);
2524
2525 return ret;
2526}
2527
Johan Hedberg744cf192011-11-08 20:40:14 +02002528int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
Johan Hedberg03811012010-12-08 00:21:06 +02002529{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002530 __le32 ev;
2531 struct cmd_lookup match = { connectable, NULL, hdev };
Johan Hedberg03811012010-12-08 00:21:06 +02002532 int ret;
2533
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002534 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
2535 &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002536
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002537 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002538
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002539 ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002540
2541 if (match.sk)
2542 sock_put(match.sk);
2543
2544 return ret;
2545}
2546
Johan Hedberg744cf192011-11-08 20:40:14 +02002547int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002548{
Johan Hedbergca69b792011-11-11 18:10:00 +02002549 u8 mgmt_err = mgmt_status(status);
2550
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002551 if (scan & SCAN_PAGE)
Johan Hedberg744cf192011-11-08 20:40:14 +02002552 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002553 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002554
2555 if (scan & SCAN_INQUIRY)
Johan Hedberg744cf192011-11-08 20:40:14 +02002556 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002557 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002558
2559 return 0;
2560}
2561
Johan Hedberg744cf192011-11-08 20:40:14 +02002562int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
2563 u8 persistent)
Johan Hedberg03811012010-12-08 00:21:06 +02002564{
Johan Hedberg86742e12011-11-07 23:13:38 +02002565 struct mgmt_ev_new_link_key ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002566
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002567 memset(&ev, 0, sizeof(ev));
Johan Hedberg03811012010-12-08 00:21:06 +02002568
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002569 ev.store_hint = persistent;
2570 bacpy(&ev.key.bdaddr, &key->bdaddr);
2571 ev.key.type = key->type;
2572 memcpy(ev.key.val, key->val, 16);
2573 ev.key.pin_len = key->pin_len;
Johan Hedberg03811012010-12-08 00:21:06 +02002574
Johan Hedberg744cf192011-11-08 20:40:14 +02002575 return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002576}
Johan Hedbergf7520542011-01-20 12:34:39 +02002577
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002578int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
2579{
2580 struct mgmt_ev_new_long_term_key ev;
2581
2582 memset(&ev, 0, sizeof(ev));
2583
2584 ev.store_hint = persistent;
2585 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2586 ev.key.addr.type = key->bdaddr_type;
2587 ev.key.authenticated = key->authenticated;
2588 ev.key.enc_size = key->enc_size;
2589 ev.key.ediv = key->ediv;
2590
2591 if (key->type == HCI_SMP_LTK)
2592 ev.key.master = 1;
2593
2594 memcpy(ev.key.rand, key->rand, sizeof(key->rand));
2595 memcpy(ev.key.val, key->val, sizeof(key->val));
2596
2597 return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
2598 &ev, sizeof(ev), NULL);
2599}
2600
Johan Hedbergafc747a2012-01-15 18:11:07 +02002601int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedbergb644ba32012-01-17 21:48:47 +02002602 u8 addr_type, u8 *name, u8 name_len,
2603 u8 *dev_class)
Johan Hedbergf7520542011-01-20 12:34:39 +02002604{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002605 char buf[512];
2606 struct mgmt_ev_device_connected *ev = (void *) buf;
2607 u16 eir_len = 0;
Johan Hedbergf7520542011-01-20 12:34:39 +02002608
Johan Hedbergb644ba32012-01-17 21:48:47 +02002609 bacpy(&ev->addr.bdaddr, bdaddr);
2610 ev->addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002611
Johan Hedbergb644ba32012-01-17 21:48:47 +02002612 if (name_len > 0)
2613 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
2614 name, name_len);
2615
2616 if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
2617 eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
2618 EIR_CLASS_OF_DEV, dev_class, 3);
2619
2620 put_unaligned_le16(eir_len, &ev->eir_len);
2621
2622 return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
2623 sizeof(*ev) + eir_len, NULL);
Johan Hedbergf7520542011-01-20 12:34:39 +02002624}
2625
Johan Hedberg8962ee72011-01-20 12:40:27 +02002626static void disconnect_rsp(struct pending_cmd *cmd, void *data)
2627{
Szymon Jancc68fb7f2011-03-22 13:12:19 +01002628 struct mgmt_cp_disconnect *cp = cmd->param;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002629 struct sock **sk = data;
Johan Hedberga38528f2011-01-22 06:46:43 +02002630 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002631
Johan Hedberg88c3df12012-02-09 14:27:38 +02002632 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2633 rp.addr.type = cp->addr.type;
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002634 rp.status = 0;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002635
Szymon Janc4e51eae2011-02-25 19:05:48 +01002636 cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02002637
2638 *sk = cmd->sk;
2639 sock_hold(*sk);
2640
Johan Hedberga664b5b2011-02-19 12:06:02 -03002641 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002642}
2643
Johan Hedberg124f6e32012-02-09 13:50:12 +02002644static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberga8a1d192011-11-10 15:54:38 +02002645{
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002646 struct hci_dev *hdev = data;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002647 struct mgmt_cp_unpair_device *cp = cmd->param;
2648 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002649
2650 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02002651 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2652 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002653
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002654 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
2655
2656 cmd_complete(cmd->sk, cmd->index, cmd->opcode, &rp, sizeof(rp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02002657
2658 mgmt_pending_remove(cmd);
2659}
2660
Johan Hedbergafc747a2012-01-15 18:11:07 +02002661int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
2662 u8 link_type, u8 addr_type)
Johan Hedbergf7520542011-01-20 12:34:39 +02002663{
Johan Hedberg4c659c32011-11-07 23:13:39 +02002664 struct mgmt_addr_info ev;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002665 struct sock *sk = NULL;
2666 int err;
2667
Johan Hedberg744cf192011-11-08 20:40:14 +02002668 mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
Johan Hedbergf7520542011-01-20 12:34:39 +02002669
Johan Hedbergf7520542011-01-20 12:34:39 +02002670 bacpy(&ev.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02002671 ev.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002672
Johan Hedbergafc747a2012-01-15 18:11:07 +02002673 err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
2674 sk);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002675
2676 if (sk)
2677 sock_put(sk);
2678
Johan Hedberg124f6e32012-02-09 13:50:12 +02002679 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002680 hdev);
Johan Hedberga8a1d192011-11-10 15:54:38 +02002681
Johan Hedberg8962ee72011-01-20 12:40:27 +02002682 return err;
2683}
2684
Johan Hedberg88c3df12012-02-09 14:27:38 +02002685int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
2686 u8 link_type, u8 addr_type, u8 status)
Johan Hedberg8962ee72011-01-20 12:40:27 +02002687{
Johan Hedberg88c3df12012-02-09 14:27:38 +02002688 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002689 struct pending_cmd *cmd;
2690 int err;
2691
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002692 cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002693 if (!cmd)
2694 return -ENOENT;
2695
Johan Hedberg88c3df12012-02-09 14:27:38 +02002696 bacpy(&rp.addr.bdaddr, bdaddr);
2697 rp.addr.type = link_to_mgmt(link_type, addr_type);
2698 rp.status = mgmt_status(status);
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002699
Johan Hedberg88c3df12012-02-09 14:27:38 +02002700 err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002701 &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02002702
Johan Hedberga664b5b2011-02-19 12:06:02 -03002703 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002704
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002705 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
2706 hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002707 return err;
Johan Hedbergf7520542011-01-20 12:34:39 +02002708}
Johan Hedberg17d5c042011-01-22 06:09:08 +02002709
Johan Hedberg48264f02011-11-09 13:58:58 +02002710int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
2711 u8 addr_type, u8 status)
Johan Hedberg17d5c042011-01-22 06:09:08 +02002712{
2713 struct mgmt_ev_connect_failed ev;
2714
Johan Hedberg4c659c32011-11-07 23:13:39 +02002715 bacpy(&ev.addr.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02002716 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02002717 ev.status = mgmt_status(status);
Johan Hedberg17d5c042011-01-22 06:09:08 +02002718
Johan Hedberg744cf192011-11-08 20:40:14 +02002719 return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg17d5c042011-01-22 06:09:08 +02002720}
Johan Hedberg980e1a52011-01-22 06:10:07 +02002721
Johan Hedberg744cf192011-11-08 20:40:14 +02002722int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
Johan Hedberg980e1a52011-01-22 06:10:07 +02002723{
2724 struct mgmt_ev_pin_code_request ev;
2725
Johan Hedberg980e1a52011-01-22 06:10:07 +02002726 bacpy(&ev.bdaddr, bdaddr);
Waldemar Rymarkiewicza770bb52011-04-28 12:07:59 +02002727 ev.secure = secure;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002728
Johan Hedberg744cf192011-11-08 20:40:14 +02002729 return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002730 NULL);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002731}
2732
Johan Hedberg744cf192011-11-08 20:40:14 +02002733int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2734 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02002735{
2736 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03002737 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002738 int err;
2739
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002740 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002741 if (!cmd)
2742 return -ENOENT;
2743
Johan Hedbergac56fb12011-02-19 12:05:59 -03002744 bacpy(&rp.bdaddr, bdaddr);
Johan Hedbergca69b792011-11-11 18:10:00 +02002745 rp.status = mgmt_status(status);
Johan Hedbergac56fb12011-02-19 12:05:59 -03002746
Johan Hedberg744cf192011-11-08 20:40:14 +02002747 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +01002748 sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02002749
Johan Hedberga664b5b2011-02-19 12:06:02 -03002750 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002751
2752 return err;
2753}
2754
Johan Hedberg744cf192011-11-08 20:40:14 +02002755int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2756 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02002757{
2758 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03002759 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002760 int err;
2761
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002762 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002763 if (!cmd)
2764 return -ENOENT;
2765
Johan Hedbergac56fb12011-02-19 12:05:59 -03002766 bacpy(&rp.bdaddr, bdaddr);
Johan Hedbergca69b792011-11-11 18:10:00 +02002767 rp.status = mgmt_status(status);
Johan Hedbergac56fb12011-02-19 12:05:59 -03002768
Johan Hedberg744cf192011-11-08 20:40:14 +02002769 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +01002770 sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02002771
Johan Hedberga664b5b2011-02-19 12:06:02 -03002772 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002773
2774 return err;
2775}
Johan Hedberga5c29682011-02-19 12:05:57 -03002776
Johan Hedberg744cf192011-11-08 20:40:14 +02002777int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002778 u8 link_type, u8 addr_type, __le32 value,
2779 u8 confirm_hint)
Johan Hedberga5c29682011-02-19 12:05:57 -03002780{
2781 struct mgmt_ev_user_confirm_request ev;
2782
Johan Hedberg744cf192011-11-08 20:40:14 +02002783 BT_DBG("%s", hdev->name);
Johan Hedberga5c29682011-02-19 12:05:57 -03002784
Johan Hedberg272d90d2012-02-09 15:26:12 +02002785 bacpy(&ev.addr.bdaddr, bdaddr);
2786 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg55bc1a32011-04-28 11:28:56 -07002787 ev.confirm_hint = confirm_hint;
Johan Hedberga5c29682011-02-19 12:05:57 -03002788 put_unaligned_le32(value, &ev.value);
2789
Johan Hedberg744cf192011-11-08 20:40:14 +02002790 return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002791 NULL);
Johan Hedberga5c29682011-02-19 12:05:57 -03002792}
2793
Johan Hedberg272d90d2012-02-09 15:26:12 +02002794int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
2795 u8 link_type, u8 addr_type)
Brian Gix604086b2011-11-23 08:28:33 -08002796{
2797 struct mgmt_ev_user_passkey_request ev;
2798
2799 BT_DBG("%s", hdev->name);
2800
Johan Hedberg272d90d2012-02-09 15:26:12 +02002801 bacpy(&ev.addr.bdaddr, bdaddr);
2802 ev.addr.type = link_to_mgmt(link_type, addr_type);
Brian Gix604086b2011-11-23 08:28:33 -08002803
2804 return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
2805 NULL);
2806}
2807
Brian Gix0df4c182011-11-16 13:53:13 -08002808static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002809 u8 link_type, u8 addr_type, u8 status,
2810 u8 opcode)
Johan Hedberga5c29682011-02-19 12:05:57 -03002811{
2812 struct pending_cmd *cmd;
2813 struct mgmt_rp_user_confirm_reply rp;
2814 int err;
2815
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002816 cmd = mgmt_pending_find(opcode, hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03002817 if (!cmd)
2818 return -ENOENT;
2819
Johan Hedberg272d90d2012-02-09 15:26:12 +02002820 bacpy(&rp.addr.bdaddr, bdaddr);
2821 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02002822 rp.status = mgmt_status(status);
Johan Hedberg744cf192011-11-08 20:40:14 +02002823 err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
Johan Hedberga5c29682011-02-19 12:05:57 -03002824
Johan Hedberga664b5b2011-02-19 12:06:02 -03002825 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03002826
2827 return err;
2828}
2829
Johan Hedberg744cf192011-11-08 20:40:14 +02002830int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002831 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03002832{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002833 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2834 status, MGMT_OP_USER_CONFIRM_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03002835}
2836
Johan Hedberg272d90d2012-02-09 15:26:12 +02002837int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2838 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03002839{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002840 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2841 status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03002842}
Johan Hedberg2a611692011-02-19 12:06:00 -03002843
Brian Gix604086b2011-11-23 08:28:33 -08002844int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002845 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08002846{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002847 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2848 status, MGMT_OP_USER_PASSKEY_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08002849}
2850
Johan Hedberg272d90d2012-02-09 15:26:12 +02002851int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2852 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08002853{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002854 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2855 status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08002856}
2857
Johan Hedbergbab73cb2012-02-09 16:07:29 +02002858int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
2859 u8 addr_type, u8 status)
Johan Hedberg2a611692011-02-19 12:06:00 -03002860{
2861 struct mgmt_ev_auth_failed ev;
2862
Johan Hedbergbab73cb2012-02-09 16:07:29 +02002863 bacpy(&ev.addr.bdaddr, bdaddr);
2864 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02002865 ev.status = mgmt_status(status);
Johan Hedberg2a611692011-02-19 12:06:00 -03002866
Johan Hedberg744cf192011-11-08 20:40:14 +02002867 return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg2a611692011-02-19 12:06:00 -03002868}
Johan Hedbergb312b1612011-03-16 14:29:37 +02002869
Johan Hedberg744cf192011-11-08 20:40:14 +02002870int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
Johan Hedbergb312b1612011-03-16 14:29:37 +02002871{
2872 struct pending_cmd *cmd;
2873 struct mgmt_cp_set_local_name ev;
2874 int err;
2875
2876 memset(&ev, 0, sizeof(ev));
2877 memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
2878
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002879 cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002880 if (!cmd)
2881 goto send_event;
2882
2883 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02002884 err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02002885 mgmt_status(status));
Johan Hedbergb312b1612011-03-16 14:29:37 +02002886 goto failed;
2887 }
2888
Johan Hedberg744cf192011-11-08 20:40:14 +02002889 update_eir(hdev);
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03002890
Johan Hedberg744cf192011-11-08 20:40:14 +02002891 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,
Johan Hedbergb312b1612011-03-16 14:29:37 +02002892 sizeof(ev));
2893 if (err < 0)
2894 goto failed;
2895
2896send_event:
Johan Hedberg744cf192011-11-08 20:40:14 +02002897 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
Johan Hedbergb312b1612011-03-16 14:29:37 +02002898 cmd ? cmd->sk : NULL);
2899
2900failed:
2901 if (cmd)
2902 mgmt_pending_remove(cmd);
2903 return err;
2904}
Szymon Jancc35938b2011-03-22 13:12:21 +01002905
Johan Hedberg744cf192011-11-08 20:40:14 +02002906int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
2907 u8 *randomizer, u8 status)
Szymon Jancc35938b2011-03-22 13:12:21 +01002908{
2909 struct pending_cmd *cmd;
2910 int err;
2911
Johan Hedberg744cf192011-11-08 20:40:14 +02002912 BT_DBG("%s status %u", hdev->name, status);
Szymon Jancc35938b2011-03-22 13:12:21 +01002913
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002914 cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002915 if (!cmd)
2916 return -ENOENT;
2917
2918 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02002919 err = cmd_status(cmd->sk, hdev->id,
Johan Hedbergca69b792011-11-11 18:10:00 +02002920 MGMT_OP_READ_LOCAL_OOB_DATA,
2921 mgmt_status(status));
Szymon Jancc35938b2011-03-22 13:12:21 +01002922 } else {
2923 struct mgmt_rp_read_local_oob_data rp;
2924
2925 memcpy(rp.hash, hash, sizeof(rp.hash));
2926 memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
2927
Johan Hedberg744cf192011-11-08 20:40:14 +02002928 err = cmd_complete(cmd->sk, hdev->id,
2929 MGMT_OP_READ_LOCAL_OOB_DATA,
2930 &rp, sizeof(rp));
Szymon Jancc35938b2011-03-22 13:12:21 +01002931 }
2932
2933 mgmt_pending_remove(cmd);
2934
2935 return err;
2936}
Johan Hedberge17acd42011-03-30 23:57:16 +03002937
Johan Hedberg48264f02011-11-09 13:58:58 +02002938int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg561aafb2012-01-04 13:31:59 +02002939 u8 addr_type, u8 *dev_class, s8 rssi,
Johan Hedberge319d2e2012-01-15 19:51:59 +02002940 u8 cfm_name, u8 *eir, u16 eir_len)
Johan Hedberge17acd42011-03-30 23:57:16 +03002941{
Johan Hedberge319d2e2012-01-15 19:51:59 +02002942 char buf[512];
2943 struct mgmt_ev_device_found *ev = (void *) buf;
Johan Hedberg1dc06092012-01-15 21:01:23 +02002944 size_t ev_size;
Johan Hedberge17acd42011-03-30 23:57:16 +03002945
Johan Hedberg1dc06092012-01-15 21:01:23 +02002946 /* Leave 5 bytes for a potential CoD field */
2947 if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
Andre Guedes7d262f82012-01-10 18:20:49 -03002948 return -EINVAL;
2949
Johan Hedberg1dc06092012-01-15 21:01:23 +02002950 memset(buf, 0, sizeof(buf));
2951
Johan Hedberge319d2e2012-01-15 19:51:59 +02002952 bacpy(&ev->addr.bdaddr, bdaddr);
2953 ev->addr.type = link_to_mgmt(link_type, addr_type);
2954 ev->rssi = rssi;
2955 ev->confirm_name = cfm_name;
Johan Hedberge17acd42011-03-30 23:57:16 +03002956
Johan Hedberg1dc06092012-01-15 21:01:23 +02002957 if (eir_len > 0)
Johan Hedberge319d2e2012-01-15 19:51:59 +02002958 memcpy(ev->eir, eir, eir_len);
Johan Hedberge17acd42011-03-30 23:57:16 +03002959
Johan Hedberg1dc06092012-01-15 21:01:23 +02002960 if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
2961 eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
2962 dev_class, 3);
2963
2964 put_unaligned_le16(eir_len, &ev->eir_len);
2965
2966 ev_size = sizeof(*ev) + eir_len;
Andre Guedesf8523592011-09-09 18:56:26 -03002967
Johan Hedberge319d2e2012-01-15 19:51:59 +02002968 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
Johan Hedberge17acd42011-03-30 23:57:16 +03002969}
Johan Hedberga88a9652011-03-30 13:18:12 +03002970
Johan Hedbergb644ba32012-01-17 21:48:47 +02002971int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
2972 u8 addr_type, s8 rssi, u8 *name, u8 name_len)
Johan Hedberga88a9652011-03-30 13:18:12 +03002973{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002974 struct mgmt_ev_device_found *ev;
2975 char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
2976 u16 eir_len;
Johan Hedberga88a9652011-03-30 13:18:12 +03002977
Johan Hedbergb644ba32012-01-17 21:48:47 +02002978 ev = (struct mgmt_ev_device_found *) buf;
Johan Hedberga88a9652011-03-30 13:18:12 +03002979
Johan Hedbergb644ba32012-01-17 21:48:47 +02002980 memset(buf, 0, sizeof(buf));
Johan Hedberga88a9652011-03-30 13:18:12 +03002981
Johan Hedbergb644ba32012-01-17 21:48:47 +02002982 bacpy(&ev->addr.bdaddr, bdaddr);
2983 ev->addr.type = link_to_mgmt(link_type, addr_type);
2984 ev->rssi = rssi;
2985
2986 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
2987 name_len);
2988
2989 put_unaligned_le16(eir_len, &ev->eir_len);
2990
Johan Hedberg053c7e02012-02-04 00:06:00 +02002991 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
2992 sizeof(*ev) + eir_len, NULL);
Johan Hedberga88a9652011-03-30 13:18:12 +03002993}
Johan Hedberg314b2382011-04-27 10:29:57 -04002994
Andre Guedes7a135102011-11-09 17:14:25 -03002995int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
Johan Hedberg164a6e72011-11-01 17:06:44 +02002996{
2997 struct pending_cmd *cmd;
2998 int err;
2999
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003000 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003001 if (!cmd)
3002 return -ENOENT;
3003
Johan Hedbergca69b792011-11-11 18:10:00 +02003004 err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003005 mgmt_pending_remove(cmd);
3006
3007 return err;
3008}
3009
Andre Guedese6d465c2011-11-09 17:14:26 -03003010int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
3011{
3012 struct pending_cmd *cmd;
3013 int err;
3014
3015 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
3016 if (!cmd)
3017 return -ENOENT;
3018
Andre Guedese75a8b0c2012-01-02 16:50:53 -03003019 err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
Johan Hedberg03811012010-12-08 00:21:06 +02003020 mgmt_pending_remove(cmd);
3021
3022 return err;
3023}
Johan Hedberg314b2382011-04-27 10:29:57 -04003024
Johan Hedberg744cf192011-11-08 20:40:14 +02003025int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
Johan Hedberg314b2382011-04-27 10:29:57 -04003026{
Johan Hedberg164a6e72011-11-01 17:06:44 +02003027 struct pending_cmd *cmd;
3028
Andre Guedes343fb142011-11-22 17:14:19 -03003029 BT_DBG("%s discovering %u", hdev->name, discovering);
3030
Johan Hedberg164a6e72011-11-01 17:06:44 +02003031 if (discovering)
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003032 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003033 else
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003034 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003035
3036 if (cmd != NULL) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003037 cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003038 mgmt_pending_remove(cmd);
3039 }
3040
Johan Hedberg744cf192011-11-08 20:40:14 +02003041 return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,
Johan Hedberg314b2382011-04-27 10:29:57 -04003042 sizeof(discovering), NULL);
3043}
Antti Julku5e762442011-08-25 16:48:02 +03003044
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003045int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003046{
3047 struct pending_cmd *cmd;
3048 struct mgmt_ev_device_blocked ev;
3049
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003050 cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003051
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003052 bacpy(&ev.addr.bdaddr, bdaddr);
3053 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003054
Johan Hedberg744cf192011-11-08 20:40:14 +02003055 return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
3056 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003057}
3058
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003059int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003060{
3061 struct pending_cmd *cmd;
3062 struct mgmt_ev_device_unblocked ev;
3063
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003064 cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003065
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003066 bacpy(&ev.addr.bdaddr, bdaddr);
3067 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003068
Johan Hedberg744cf192011-11-08 20:40:14 +02003069 return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
3070 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003071}