blob: 0cf0f4dc8213dc441608b7cc2b9aa5792550febd [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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-12-13 21:07:06 +0200158 kfree_skb(skb);
159
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300160 return err;
Johan Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-12-13 21:07:06 +0200308
Johan Hedberg84bde9d2012-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 Hedbergf7b64e62010-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 Hedberg84bde9d2012-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-12-13 21:07:06 +0200692
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300693 hci_dev_lock(hdev);
Johan Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedbergf7b64e62010-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 Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-01-17 14:41:05 +02001040 }
1041
Szymon Janc4e51eae2011-02-25 19:05:48 +01001042 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-01-17 14:41:05 +02001048 key_count);
1049
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001050 hci_dev_lock(hdev);
Johan Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-01-17 14:41:05 +02001058 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001059 clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-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 Hedberg55ed8ca2011-01-17 14:41:05 +02001071 hci_dev_put(hdev);
1072
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001073 return 0;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001074}
1075
Johan Hedberg124f6e32012-02-09 13:50:12 +02001076static int unpair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001077{
1078 struct hci_dev *hdev;
Johan Hedberg124f6e32012-02-09 13:50:12 +02001079 struct mgmt_cp_unpair_device *cp = data;
1080 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001081 struct hci_cp_disconnect dc;
1082 struct pending_cmd *cmd;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001083 struct hci_conn *conn;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001084 int err;
1085
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001086 if (len != sizeof(*cp))
Johan Hedberg124f6e32012-02-09 13:50:12 +02001087 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001088 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001089
Szymon Janc4e51eae2011-02-25 19:05:48 +01001090 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001091 if (!hdev)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001092 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001093 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001094
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001095 hci_dev_lock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001096
Johan Hedberga8a1d192011-11-10 15:54:38 +02001097 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02001098 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1099 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001100
Johan Hedberg124f6e32012-02-09 13:50:12 +02001101 if (cp->addr.type == MGMT_ADDR_BREDR)
1102 err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
1103 else
1104 err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
Vinicius Costa Gomesb0dbfb42012-02-02 21:08:03 -03001105
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001106 if (err < 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001107 rp.status = MGMT_STATUS_NOT_PAIRED;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001108 goto unlock;
1109 }
1110
Johan Hedberga8a1d192011-11-10 15:54:38 +02001111 if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
Johan Hedberg124f6e32012-02-09 13:50:12 +02001112 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001113 sizeof(rp));
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001114 goto unlock;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001115 }
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001116
Johan Hedberg124f6e32012-02-09 13:50:12 +02001117 if (cp->addr.type == MGMT_ADDR_BREDR)
1118 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
1119 &cp->addr.bdaddr);
1120 else
1121 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
1122 &cp->addr.bdaddr);
1123
Johan Hedberga8a1d192011-11-10 15:54:38 +02001124 if (!conn) {
Johan Hedberg124f6e32012-02-09 13:50:12 +02001125 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001126 sizeof(rp));
1127 goto unlock;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001128 }
1129
Johan Hedberg124f6e32012-02-09 13:50:12 +02001130 cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
1131 sizeof(*cp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02001132 if (!cmd) {
1133 err = -ENOMEM;
1134 goto unlock;
1135 }
1136
1137 put_unaligned_le16(conn->handle, &dc.handle);
1138 dc.reason = 0x13; /* Remote User Terminated Connection */
1139 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1140 if (err < 0)
1141 mgmt_pending_remove(cmd);
1142
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001143unlock:
Johan Hedbergca69b792011-11-11 18:10:00 +02001144 if (err < 0)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001145 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001146 sizeof(rp));
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001147 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001148 hci_dev_put(hdev);
1149
1150 return err;
1151}
1152
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001153static int disconnect(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg8962ee72011-01-20 12:40:27 +02001154{
1155 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001156 struct mgmt_cp_disconnect *cp = data;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001157 struct hci_cp_disconnect dc;
Johan Hedberg366a0332011-02-19 12:05:55 -03001158 struct pending_cmd *cmd;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001159 struct hci_conn *conn;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001160 int err;
1161
1162 BT_DBG("");
1163
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001164 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001165 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1166 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001167
Szymon Janc4e51eae2011-02-25 19:05:48 +01001168 hdev = hci_dev_get(index);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001169 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001170 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1171 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001172
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001173 hci_dev_lock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001174
1175 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001176 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1177 MGMT_STATUS_NOT_POWERED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001178 goto failed;
1179 }
1180
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001181 if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001182 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1183 MGMT_STATUS_BUSY);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001184 goto failed;
1185 }
1186
Johan Hedberg88c3df12012-02-09 14:27:38 +02001187 if (cp->addr.type == MGMT_ADDR_BREDR)
1188 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
1189 else
1190 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
Vinicius Costa Gomes365227e2011-05-06 18:41:44 -03001191
Johan Hedberg8962ee72011-01-20 12:40:27 +02001192 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001193 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1194 MGMT_STATUS_NOT_CONNECTED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001195 goto failed;
1196 }
1197
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001198 cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001199 if (!cmd) {
1200 err = -ENOMEM;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001201 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001202 }
Johan Hedberg8962ee72011-01-20 12:40:27 +02001203
1204 put_unaligned_le16(conn->handle, &dc.handle);
1205 dc.reason = 0x13; /* Remote User Terminated Connection */
1206
1207 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1208 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001209 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001210
1211failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001212 hci_dev_unlock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001213 hci_dev_put(hdev);
1214
1215 return err;
1216}
1217
Johan Hedberg48264f02011-11-09 13:58:58 +02001218static u8 link_to_mgmt(u8 link_type, u8 addr_type)
Johan Hedberg4c659c32011-11-07 23:13:39 +02001219{
1220 switch (link_type) {
1221 case LE_LINK:
Johan Hedberg48264f02011-11-09 13:58:58 +02001222 switch (addr_type) {
1223 case ADDR_LE_DEV_PUBLIC:
1224 return MGMT_ADDR_LE_PUBLIC;
1225 case ADDR_LE_DEV_RANDOM:
1226 return MGMT_ADDR_LE_RANDOM;
1227 default:
1228 return MGMT_ADDR_INVALID;
1229 }
Johan Hedberg4c659c32011-11-07 23:13:39 +02001230 case ACL_LINK:
1231 return MGMT_ADDR_BREDR;
1232 default:
1233 return MGMT_ADDR_INVALID;
1234 }
1235}
1236
Szymon Janc8ce62842011-03-01 16:55:32 +01001237static int get_connections(struct sock *sk, u16 index)
Johan Hedberg2784eb42011-01-21 13:56:35 +02001238{
Johan Hedberg2784eb42011-01-21 13:56:35 +02001239 struct mgmt_rp_get_connections *rp;
1240 struct hci_dev *hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001241 struct hci_conn *c;
Johan Hedberga38528f2011-01-22 06:46:43 +02001242 size_t rp_len;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001243 u16 count;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001244 int i, err;
1245
1246 BT_DBG("");
1247
Szymon Janc4e51eae2011-02-25 19:05:48 +01001248 hdev = hci_dev_get(index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001249 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001250 return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
1251 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001252
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001253 hci_dev_lock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001254
1255 count = 0;
Johan Hedbergb644ba32012-01-17 21:48:47 +02001256 list_for_each_entry(c, &hdev->conn_hash.list, list) {
1257 if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1258 count++;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001259 }
1260
Johan Hedberg4c659c32011-11-07 23:13:39 +02001261 rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
Johan Hedberga38528f2011-01-22 06:46:43 +02001262 rp = kmalloc(rp_len, GFP_ATOMIC);
1263 if (!rp) {
Johan Hedberg2784eb42011-01-21 13:56:35 +02001264 err = -ENOMEM;
1265 goto unlock;
1266 }
1267
Johan Hedberg2784eb42011-01-21 13:56:35 +02001268 put_unaligned_le16(count, &rp->conn_count);
1269
Johan Hedberg2784eb42011-01-21 13:56:35 +02001270 i = 0;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001271 list_for_each_entry(c, &hdev->conn_hash.list, list) {
Johan Hedbergb644ba32012-01-17 21:48:47 +02001272 if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1273 continue;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001274 bacpy(&rp->addr[i].bdaddr, &c->dst);
Johan Hedberg48264f02011-11-09 13:58:58 +02001275 rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
Johan Hedberg4c659c32011-11-07 23:13:39 +02001276 if (rp->addr[i].type == MGMT_ADDR_INVALID)
1277 continue;
1278 i++;
1279 }
1280
1281 /* Recalculate length in case of filtered SCO connections, etc */
1282 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberg2784eb42011-01-21 13:56:35 +02001283
Szymon Janc4e51eae2011-02-25 19:05:48 +01001284 err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001285
1286unlock:
Johan Hedberga38528f2011-01-22 06:46:43 +02001287 kfree(rp);
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001288 hci_dev_unlock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001289 hci_dev_put(hdev);
1290 return err;
1291}
1292
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001293static int send_pin_code_neg_reply(struct sock *sk, u16 index,
1294 struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
1295{
1296 struct pending_cmd *cmd;
1297 int err;
1298
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001299 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001300 sizeof(*cp));
1301 if (!cmd)
1302 return -ENOMEM;
1303
1304 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
1305 &cp->bdaddr);
1306 if (err < 0)
1307 mgmt_pending_remove(cmd);
1308
1309 return err;
1310}
1311
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001312static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001313{
1314 struct hci_dev *hdev;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001315 struct hci_conn *conn;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001316 struct mgmt_cp_pin_code_reply *cp = data;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001317 struct mgmt_cp_pin_code_neg_reply ncp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001318 struct hci_cp_pin_code_reply reply;
Johan Hedberg366a0332011-02-19 12:05:55 -03001319 struct pending_cmd *cmd;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001320 int err;
1321
1322 BT_DBG("");
1323
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001324 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001325 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1326 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001327
Szymon Janc4e51eae2011-02-25 19:05:48 +01001328 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001329 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001330 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1331 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001332
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001333 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001334
1335 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001336 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1337 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001338 goto failed;
1339 }
1340
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001341 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
1342 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001343 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1344 MGMT_STATUS_NOT_CONNECTED);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001345 goto failed;
1346 }
1347
1348 if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
1349 bacpy(&ncp.bdaddr, &cp->bdaddr);
1350
1351 BT_ERR("PIN code is not 16 bytes long");
1352
1353 err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
1354 if (err >= 0)
1355 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001356 MGMT_STATUS_INVALID_PARAMS);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001357
1358 goto failed;
1359 }
1360
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001361 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data,
1362 len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001363 if (!cmd) {
1364 err = -ENOMEM;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001365 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001366 }
Johan Hedberg980e1a52011-01-22 06:10:07 +02001367
1368 bacpy(&reply.bdaddr, &cp->bdaddr);
1369 reply.pin_len = cp->pin_len;
Waldemar Rymarkiewicz24718ca2011-06-01 17:28:47 +02001370 memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
Johan Hedberg980e1a52011-01-22 06:10:07 +02001371
1372 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
1373 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001374 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001375
1376failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001377 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001378 hci_dev_put(hdev);
1379
1380 return err;
1381}
1382
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001383static int pin_code_neg_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001384{
1385 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001386 struct mgmt_cp_pin_code_neg_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001387 int err;
1388
1389 BT_DBG("");
1390
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001391 if (len != sizeof(*cp))
1392 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001393 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001394
Szymon Janc4e51eae2011-02-25 19:05:48 +01001395 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001396 if (!hdev)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001397 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001398 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001399
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001400 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001401
1402 if (!test_bit(HCI_UP, &hdev->flags)) {
Szymon Janc4e51eae2011-02-25 19:05:48 +01001403 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001404 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001405 goto failed;
1406 }
1407
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001408 err = send_pin_code_neg_reply(sk, index, hdev, cp);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001409
1410failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001411 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001412 hci_dev_put(hdev);
1413
1414 return err;
1415}
1416
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001417static int set_io_capability(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001418{
1419 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001420 struct mgmt_cp_set_io_capability *cp = data;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001421
1422 BT_DBG("");
1423
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001424 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001425 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1426 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001427
Szymon Janc4e51eae2011-02-25 19:05:48 +01001428 hdev = hci_dev_get(index);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001429 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001430 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1431 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001432
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001433 hci_dev_lock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001434
1435 hdev->io_capability = cp->io_capability;
1436
1437 BT_DBG("%s IO capability set to 0x%02x", hdev->name,
Szymon Jancb8534e0f2011-03-01 16:55:34 +01001438 hdev->io_capability);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001439
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001440 hci_dev_unlock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001441 hci_dev_put(hdev);
1442
Szymon Janc4e51eae2011-02-25 19:05:48 +01001443 return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001444}
1445
Johan Hedberge9a416b2011-02-19 12:05:56 -03001446static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1447{
1448 struct hci_dev *hdev = conn->hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001449 struct pending_cmd *cmd;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001450
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001451 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
Johan Hedberge9a416b2011-02-19 12:05:56 -03001452 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1453 continue;
1454
Johan Hedberge9a416b2011-02-19 12:05:56 -03001455 if (cmd->user_data != conn)
1456 continue;
1457
1458 return cmd;
1459 }
1460
1461 return NULL;
1462}
1463
1464static void pairing_complete(struct pending_cmd *cmd, u8 status)
1465{
1466 struct mgmt_rp_pair_device rp;
1467 struct hci_conn *conn = cmd->user_data;
1468
Johan Hedbergba4e5642011-11-11 00:07:34 +02001469 bacpy(&rp.addr.bdaddr, &conn->dst);
1470 rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001471 rp.status = status;
1472
Szymon Janc4e51eae2011-02-25 19:05:48 +01001473 cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001474
1475 /* So we don't get further callbacks for this connection */
1476 conn->connect_cfm_cb = NULL;
1477 conn->security_cfm_cb = NULL;
1478 conn->disconn_cfm_cb = NULL;
1479
1480 hci_conn_put(conn);
1481
Johan Hedberga664b5b2011-02-19 12:06:02 -03001482 mgmt_pending_remove(cmd);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001483}
1484
1485static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1486{
1487 struct pending_cmd *cmd;
1488
1489 BT_DBG("status %u", status);
1490
Johan Hedberg56e5cb82011-11-08 20:40:16 +02001491 cmd = find_pairing(conn);
1492 if (!cmd)
1493 BT_DBG("Unable to find a pending command");
1494 else
1495 pairing_complete(cmd, status);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001496}
1497
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001498static int pair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001499{
1500 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001501 struct mgmt_cp_pair_device *cp = data;
Johan Hedberg1425acb2011-11-11 00:07:35 +02001502 struct mgmt_rp_pair_device rp;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001503 struct pending_cmd *cmd;
1504 u8 sec_level, auth_type;
1505 struct hci_conn *conn;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001506 int err;
1507
1508 BT_DBG("");
1509
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001510 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001511 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1512 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001513
Szymon Janc4e51eae2011-02-25 19:05:48 +01001514 hdev = hci_dev_get(index);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001515 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001516 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1517 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001518
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001519 hci_dev_lock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001520
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001521 sec_level = BT_SECURITY_MEDIUM;
1522 if (cp->io_cap == 0x03)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001523 auth_type = HCI_AT_DEDICATED_BONDING;
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001524 else
Johan Hedberge9a416b2011-02-19 12:05:56 -03001525 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001526
Johan Hedbergba4e5642011-11-11 00:07:34 +02001527 if (cp->addr.type == MGMT_ADDR_BREDR)
1528 conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001529 auth_type);
1530 else
Johan Hedbergba4e5642011-11-11 00:07:34 +02001531 conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001532 auth_type);
1533
Johan Hedberg1425acb2011-11-11 00:07:35 +02001534 memset(&rp, 0, sizeof(rp));
1535 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1536 rp.addr.type = cp->addr.type;
1537
Ville Tervo30e76272011-02-22 16:10:53 -03001538 if (IS_ERR(conn)) {
Johan Hedberg1425acb2011-11-11 00:07:35 +02001539 rp.status = -PTR_ERR(conn);
1540 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1541 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001542 goto unlock;
1543 }
1544
1545 if (conn->connect_cfm_cb) {
1546 hci_conn_put(conn);
Johan Hedberg1425acb2011-11-11 00:07:35 +02001547 rp.status = EBUSY;
1548 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1549 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001550 goto unlock;
1551 }
1552
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001553 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001554 if (!cmd) {
1555 err = -ENOMEM;
1556 hci_conn_put(conn);
1557 goto unlock;
1558 }
1559
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001560 /* For LE, just connecting isn't a proof that the pairing finished */
Johan Hedbergba4e5642011-11-11 00:07:34 +02001561 if (cp->addr.type == MGMT_ADDR_BREDR)
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001562 conn->connect_cfm_cb = pairing_complete_cb;
1563
Johan Hedberge9a416b2011-02-19 12:05:56 -03001564 conn->security_cfm_cb = pairing_complete_cb;
1565 conn->disconn_cfm_cb = pairing_complete_cb;
1566 conn->io_capability = cp->io_cap;
1567 cmd->user_data = conn;
1568
1569 if (conn->state == BT_CONNECTED &&
1570 hci_conn_security(conn, sec_level, auth_type))
1571 pairing_complete(cmd, 0);
1572
1573 err = 0;
1574
1575unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001576 hci_dev_unlock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001577 hci_dev_put(hdev);
1578
1579 return err;
1580}
1581
Johan Hedberg28424702012-02-02 04:02:29 +02001582static int cancel_pair_device(struct sock *sk, u16 index,
1583 unsigned char *data, u16 len)
1584{
1585 struct mgmt_addr_info *addr = (void *) data;
1586 struct hci_dev *hdev;
1587 struct pending_cmd *cmd;
1588 struct hci_conn *conn;
1589 int err;
1590
1591 BT_DBG("");
1592
1593 if (len != sizeof(*addr))
1594 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1595 MGMT_STATUS_INVALID_PARAMS);
1596
1597 hdev = hci_dev_get(index);
1598 if (!hdev)
1599 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1600 MGMT_STATUS_INVALID_PARAMS);
1601
1602 hci_dev_lock(hdev);
1603
1604 cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
1605 if (!cmd) {
1606 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1607 MGMT_STATUS_INVALID_PARAMS);
1608 goto unlock;
1609 }
1610
1611 conn = cmd->user_data;
1612
1613 if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
1614 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1615 MGMT_STATUS_INVALID_PARAMS);
1616 goto unlock;
1617 }
1618
1619 pairing_complete(cmd, MGMT_STATUS_CANCELLED);
1620
1621 err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, addr,
1622 sizeof(*addr));
1623unlock:
1624 hci_dev_unlock(hdev);
1625 hci_dev_put(hdev);
1626
1627 return err;
1628}
1629
Brian Gix0df4c182011-11-16 13:53:13 -08001630static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02001631 u8 type, u16 mgmt_op, u16 hci_op,
1632 __le32 passkey)
Johan Hedberga5c29682011-02-19 12:05:57 -03001633{
Johan Hedberga5c29682011-02-19 12:05:57 -03001634 struct pending_cmd *cmd;
1635 struct hci_dev *hdev;
Brian Gix0df4c182011-11-16 13:53:13 -08001636 struct hci_conn *conn;
Johan Hedberga5c29682011-02-19 12:05:57 -03001637 int err;
1638
Szymon Janc4e51eae2011-02-25 19:05:48 +01001639 hdev = hci_dev_get(index);
Johan Hedberga5c29682011-02-19 12:05:57 -03001640 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001641 return cmd_status(sk, index, mgmt_op,
1642 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberga5c29682011-02-19 12:05:57 -03001643
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001644 hci_dev_lock(hdev);
Johan Hedberg08ba5382011-03-16 14:29:34 +02001645
Johan Hedberga5c29682011-02-19 12:05:57 -03001646 if (!test_bit(HCI_UP, &hdev->flags)) {
Brian Gix0df4c182011-11-16 13:53:13 -08001647 err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
1648 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001649 }
1650
Johan Hedberg272d90d2012-02-09 15:26:12 +02001651 if (type == MGMT_ADDR_BREDR)
1652 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
1653 else
Brian Gix47c15e22011-11-16 13:53:14 -08001654 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
Brian Gix47c15e22011-11-16 13:53:14 -08001655
Johan Hedberg272d90d2012-02-09 15:26:12 +02001656 if (!conn) {
1657 err = cmd_status(sk, index, mgmt_op,
1658 MGMT_STATUS_NOT_CONNECTED);
1659 goto done;
1660 }
1661
1662 if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
Brian Gix47c15e22011-11-16 13:53:14 -08001663 /* Continue with pairing via SMP */
Brian Gix5fe57d92011-12-21 16:12:13 -08001664 err = smp_user_confirm_reply(conn, mgmt_op, passkey);
Brian Gix47c15e22011-11-16 13:53:14 -08001665
Brian Gix5fe57d92011-12-21 16:12:13 -08001666 if (!err)
1667 err = cmd_status(sk, index, mgmt_op,
1668 MGMT_STATUS_SUCCESS);
1669 else
1670 err = cmd_status(sk, index, mgmt_op,
1671 MGMT_STATUS_FAILED);
1672
Brian Gix47c15e22011-11-16 13:53:14 -08001673 goto done;
1674 }
1675
Brian Gix0df4c182011-11-16 13:53:13 -08001676 cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
Johan Hedberga5c29682011-02-19 12:05:57 -03001677 if (!cmd) {
1678 err = -ENOMEM;
Brian Gix0df4c182011-11-16 13:53:13 -08001679 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001680 }
1681
Brian Gix0df4c182011-11-16 13:53:13 -08001682 /* Continue with pairing via HCI */
Brian Gix604086b2011-11-23 08:28:33 -08001683 if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
1684 struct hci_cp_user_passkey_reply cp;
1685
1686 bacpy(&cp.bdaddr, bdaddr);
1687 cp.passkey = passkey;
1688 err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
1689 } else
1690 err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
1691
Johan Hedberga664b5b2011-02-19 12:06:02 -03001692 if (err < 0)
1693 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03001694
Brian Gix0df4c182011-11-16 13:53:13 -08001695done:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001696 hci_dev_unlock(hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03001697 hci_dev_put(hdev);
1698
1699 return err;
1700}
1701
Brian Gix0df4c182011-11-16 13:53:13 -08001702static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
1703{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001704 struct mgmt_cp_user_confirm_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001705
1706 BT_DBG("");
1707
1708 if (len != sizeof(*cp))
1709 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
1710 MGMT_STATUS_INVALID_PARAMS);
1711
Johan Hedberg272d90d2012-02-09 15:26:12 +02001712 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1713 MGMT_OP_USER_CONFIRM_REPLY,
1714 HCI_OP_USER_CONFIRM_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001715}
1716
1717static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
1718 u16 len)
1719{
Johan Hedbergc9c26592011-12-15 00:47:41 +02001720 struct mgmt_cp_user_confirm_neg_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001721
1722 BT_DBG("");
1723
1724 if (len != sizeof(*cp))
1725 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
1726 MGMT_STATUS_INVALID_PARAMS);
1727
Johan Hedberg272d90d2012-02-09 15:26:12 +02001728 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1729 MGMT_OP_USER_CONFIRM_NEG_REPLY,
1730 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001731}
1732
Brian Gix604086b2011-11-23 08:28:33 -08001733static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
1734{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001735 struct mgmt_cp_user_passkey_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001736
1737 BT_DBG("");
1738
1739 if (len != sizeof(*cp))
1740 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
1741 EINVAL);
1742
Johan Hedberg272d90d2012-02-09 15:26:12 +02001743 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1744 MGMT_OP_USER_PASSKEY_REPLY,
1745 HCI_OP_USER_PASSKEY_REPLY,
1746 cp->passkey);
Brian Gix604086b2011-11-23 08:28:33 -08001747}
1748
1749static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
1750 u16 len)
1751{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001752 struct mgmt_cp_user_passkey_neg_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001753
1754 BT_DBG("");
1755
1756 if (len != sizeof(*cp))
1757 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
1758 EINVAL);
1759
Johan Hedberg272d90d2012-02-09 15:26:12 +02001760 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1761 MGMT_OP_USER_PASSKEY_NEG_REPLY,
1762 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
Brian Gix604086b2011-11-23 08:28:33 -08001763}
1764
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001765static int set_local_name(struct sock *sk, u16 index, void *data,
Johan Hedbergb312b1612011-03-16 14:29:37 +02001766 u16 len)
1767{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001768 struct mgmt_cp_set_local_name *mgmt_cp = data;
Johan Hedbergb312b1612011-03-16 14:29:37 +02001769 struct hci_cp_write_local_name hci_cp;
1770 struct hci_dev *hdev;
1771 struct pending_cmd *cmd;
1772 int err;
1773
1774 BT_DBG("");
1775
1776 if (len != sizeof(*mgmt_cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001777 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
1778 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001779
1780 hdev = hci_dev_get(index);
1781 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001782 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
1783 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001784
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001785 hci_dev_lock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001786
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001787 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data,
1788 len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001789 if (!cmd) {
1790 err = -ENOMEM;
1791 goto failed;
1792 }
1793
1794 memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
1795 err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
1796 &hci_cp);
1797 if (err < 0)
1798 mgmt_pending_remove(cmd);
1799
1800failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001801 hci_dev_unlock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02001802 hci_dev_put(hdev);
1803
1804 return err;
1805}
1806
Szymon Jancc35938b2011-03-22 13:12:21 +01001807static int read_local_oob_data(struct sock *sk, u16 index)
1808{
1809 struct hci_dev *hdev;
1810 struct pending_cmd *cmd;
1811 int err;
1812
1813 BT_DBG("hci%u", index);
1814
1815 hdev = hci_dev_get(index);
1816 if (!hdev)
1817 return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001818 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancc35938b2011-03-22 13:12:21 +01001819
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001820 hci_dev_lock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01001821
1822 if (!test_bit(HCI_UP, &hdev->flags)) {
1823 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001824 MGMT_STATUS_NOT_POWERED);
Szymon Jancc35938b2011-03-22 13:12:21 +01001825 goto unlock;
1826 }
1827
1828 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
1829 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001830 MGMT_STATUS_NOT_SUPPORTED);
Szymon Jancc35938b2011-03-22 13:12:21 +01001831 goto unlock;
1832 }
1833
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001834 if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001835 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
1836 MGMT_STATUS_BUSY);
Szymon Jancc35938b2011-03-22 13:12:21 +01001837 goto unlock;
1838 }
1839
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001840 cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
Szymon Jancc35938b2011-03-22 13:12:21 +01001841 if (!cmd) {
1842 err = -ENOMEM;
1843 goto unlock;
1844 }
1845
1846 err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
1847 if (err < 0)
1848 mgmt_pending_remove(cmd);
1849
1850unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001851 hci_dev_unlock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01001852 hci_dev_put(hdev);
1853
1854 return err;
1855}
1856
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001857static int add_remote_oob_data(struct sock *sk, u16 index, void *data,
1858 u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01001859{
1860 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001861 struct mgmt_cp_add_remote_oob_data *cp = data;
Szymon Janc2763eda2011-03-22 13:12:22 +01001862 int err;
1863
1864 BT_DBG("hci%u ", index);
1865
1866 if (len != sizeof(*cp))
1867 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001868 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001869
1870 hdev = hci_dev_get(index);
1871 if (!hdev)
1872 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001873 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001874
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001875 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001876
Johan Hedberg664ce4c2012-02-09 15:44:09 +02001877 err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
Szymon Janc2763eda2011-03-22 13:12:22 +01001878 cp->randomizer);
1879 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02001880 err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
1881 MGMT_STATUS_FAILED);
Szymon Janc2763eda2011-03-22 13:12:22 +01001882 else
1883 err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
1884 0);
1885
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001886 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001887 hci_dev_put(hdev);
1888
1889 return err;
1890}
1891
1892static int remove_remote_oob_data(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001893 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01001894{
1895 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001896 struct mgmt_cp_remove_remote_oob_data *cp = data;
Szymon Janc2763eda2011-03-22 13:12:22 +01001897 int err;
1898
1899 BT_DBG("hci%u ", index);
1900
1901 if (len != sizeof(*cp))
1902 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001903 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001904
1905 hdev = hci_dev_get(index);
1906 if (!hdev)
1907 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001908 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001909
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001910 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001911
Johan Hedberg664ce4c2012-02-09 15:44:09 +02001912 err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
Szymon Janc2763eda2011-03-22 13:12:22 +01001913 if (err < 0)
1914 err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02001915 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01001916 else
1917 err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
1918 NULL, 0);
1919
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001920 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01001921 hci_dev_put(hdev);
1922
1923 return err;
1924}
1925
Johan Hedberg450dfda2011-11-12 11:58:22 +02001926static int start_discovery(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001927 void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04001928{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001929 struct mgmt_cp_start_discovery *cp = data;
Andre Guedes3fd24152012-02-03 17:48:01 -03001930 unsigned long discov_type = cp->type;
Johan Hedberg14a53662011-04-27 10:29:56 -04001931 struct pending_cmd *cmd;
1932 struct hci_dev *hdev;
1933 int err;
1934
1935 BT_DBG("hci%u", index);
1936
Johan Hedberg450dfda2011-11-12 11:58:22 +02001937 if (len != sizeof(*cp))
1938 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1939 MGMT_STATUS_INVALID_PARAMS);
1940
Johan Hedberg14a53662011-04-27 10:29:56 -04001941 hdev = hci_dev_get(index);
1942 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001943 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1944 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04001945
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001946 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04001947
Johan Hedbergbd2d1332011-11-07 23:13:37 +02001948 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001949 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1950 MGMT_STATUS_NOT_POWERED);
Johan Hedbergbd2d1332011-11-07 23:13:37 +02001951 goto failed;
1952 }
1953
Johan Hedbergff9ef572012-01-04 14:23:45 +02001954 if (hdev->discovery.state != DISCOVERY_STOPPED) {
1955 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
1956 MGMT_STATUS_BUSY);
1957 goto failed;
1958 }
1959
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001960 cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04001961 if (!cmd) {
1962 err = -ENOMEM;
1963 goto failed;
1964 }
1965
Andre Guedes3fd24152012-02-03 17:48:01 -03001966 if (test_bit(MGMT_ADDR_BREDR, &discov_type))
1967 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
1968 else if (test_bit(MGMT_ADDR_LE_PUBLIC, &discov_type) &&
1969 test_bit(MGMT_ADDR_LE_RANDOM, &discov_type))
1970 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
1971 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
1972 else
1973 err = -EINVAL;
1974
Johan Hedberg14a53662011-04-27 10:29:56 -04001975 if (err < 0)
1976 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02001977 else
1978 hci_discovery_set_state(hdev, DISCOVERY_STARTING);
Johan Hedberg14a53662011-04-27 10:29:56 -04001979
1980failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001981 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04001982 hci_dev_put(hdev);
1983
1984 return err;
1985}
1986
1987static int stop_discovery(struct sock *sk, u16 index)
1988{
1989 struct hci_dev *hdev;
1990 struct pending_cmd *cmd;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02001991 struct hci_cp_remote_name_req_cancel cp;
1992 struct inquiry_entry *e;
Johan Hedberg14a53662011-04-27 10:29:56 -04001993 int err;
1994
1995 BT_DBG("hci%u", index);
1996
1997 hdev = hci_dev_get(index);
1998 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001999 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2000 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002001
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002002 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002003
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002004 if (!hci_discovery_active(hdev)) {
Johan Hedbergff9ef572012-01-04 14:23:45 +02002005 err = cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2006 MGMT_STATUS_REJECTED);
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002007 goto unlock;
Johan Hedbergff9ef572012-01-04 14:23:45 +02002008 }
2009
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002010 cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002011 if (!cmd) {
2012 err = -ENOMEM;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002013 goto unlock;
Johan Hedberg14a53662011-04-27 10:29:56 -04002014 }
2015
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002016 if (hdev->discovery.state == DISCOVERY_INQUIRY) {
2017 err = hci_cancel_inquiry(hdev);
2018 if (err < 0)
2019 mgmt_pending_remove(cmd);
2020 else
2021 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
2022 goto unlock;
2023 }
2024
2025 e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
2026 if (!e) {
2027 mgmt_pending_remove(cmd);
2028 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, NULL, 0);
2029 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2030 goto unlock;
2031 }
2032
2033 bacpy(&cp.bdaddr, &e->data.bdaddr);
2034 err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
2035 sizeof(cp), &cp);
Johan Hedberg14a53662011-04-27 10:29:56 -04002036 if (err < 0)
2037 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002038 else
2039 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002040
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002041unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002042 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002043 hci_dev_put(hdev);
2044
2045 return err;
2046}
2047
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002048static int confirm_name(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg561aafb2012-01-04 13:31:59 +02002049{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002050 struct mgmt_cp_confirm_name *cp = data;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002051 struct inquiry_entry *e;
2052 struct hci_dev *hdev;
2053 int err;
2054
2055 BT_DBG("hci%u", index);
2056
2057 if (len != sizeof(*cp))
2058 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2059 MGMT_STATUS_INVALID_PARAMS);
2060
2061 hdev = hci_dev_get(index);
2062 if (!hdev)
2063 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2064 MGMT_STATUS_INVALID_PARAMS);
2065
2066 hci_dev_lock(hdev);
2067
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002068 if (!hci_discovery_active(hdev)) {
2069 err = cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2070 MGMT_STATUS_FAILED);
2071 goto failed;
2072 }
2073
Johan Hedberg561aafb2012-01-04 13:31:59 +02002074 e = hci_inquiry_cache_lookup_unknown(hdev, &cp->bdaddr);
2075 if (!e) {
2076 err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME,
2077 MGMT_STATUS_INVALID_PARAMS);
2078 goto failed;
2079 }
2080
2081 if (cp->name_known) {
2082 e->name_state = NAME_KNOWN;
2083 list_del(&e->list);
2084 } else {
2085 e->name_state = NAME_NEEDED;
Johan Hedberga3d4e202012-01-09 00:53:02 +02002086 hci_inquiry_cache_update_resolve(hdev, e);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002087 }
2088
2089 err = 0;
2090
2091failed:
2092 hci_dev_unlock(hdev);
2093
2094 return err;
2095}
2096
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002097static int block_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002098{
2099 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002100 struct mgmt_cp_block_device *cp = data;
Antti Julku7fbec222011-06-15 12:01:15 +03002101 int err;
2102
2103 BT_DBG("hci%u", index);
2104
Antti Julku7fbec222011-06-15 12:01:15 +03002105 if (len != sizeof(*cp))
2106 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002107 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002108
2109 hdev = hci_dev_get(index);
2110 if (!hdev)
2111 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002112 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002113
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002114 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002115
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002116 err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002117 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02002118 err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
2119 MGMT_STATUS_FAILED);
Antti Julku7fbec222011-06-15 12:01:15 +03002120 else
2121 err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
2122 NULL, 0);
Antti Julku5e762442011-08-25 16:48:02 +03002123
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002124 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002125 hci_dev_put(hdev);
2126
2127 return err;
2128}
2129
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002130static int unblock_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002131{
2132 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002133 struct mgmt_cp_unblock_device *cp = data;
Antti Julku7fbec222011-06-15 12:01:15 +03002134 int err;
2135
2136 BT_DBG("hci%u", index);
2137
Antti Julku7fbec222011-06-15 12:01:15 +03002138 if (len != sizeof(*cp))
2139 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002140 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002141
2142 hdev = hci_dev_get(index);
2143 if (!hdev)
2144 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002145 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002146
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002147 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002148
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002149 err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002150
2151 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02002152 err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2153 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002154 else
2155 err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2156 NULL, 0);
Antti Julku5e762442011-08-25 16:48:02 +03002157
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002158 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002159 hci_dev_put(hdev);
2160
2161 return err;
2162}
2163
Antti Julkuf6422ec2011-06-22 13:11:56 +03002164static int set_fast_connectable(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002165 void *data, u16 len)
Antti Julkuf6422ec2011-06-22 13:11:56 +03002166{
2167 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002168 struct mgmt_mode *cp = data;
Antti Julkuf6422ec2011-06-22 13:11:56 +03002169 struct hci_cp_write_page_scan_activity acp;
2170 u8 type;
2171 int err;
2172
2173 BT_DBG("hci%u", index);
2174
2175 if (len != sizeof(*cp))
2176 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002177 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002178
2179 hdev = hci_dev_get(index);
2180 if (!hdev)
2181 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002182 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002183
2184 hci_dev_lock(hdev);
2185
Johan Hedbergf7c68692011-12-15 00:47:36 +02002186 if (cp->val) {
Antti Julkuf6422ec2011-06-22 13:11:56 +03002187 type = PAGE_SCAN_TYPE_INTERLACED;
2188 acp.interval = 0x0024; /* 22.5 msec page scan interval */
2189 } else {
2190 type = PAGE_SCAN_TYPE_STANDARD; /* default */
2191 acp.interval = 0x0800; /* default 1.28 sec page scan */
2192 }
2193
2194 acp.window = 0x0012; /* default 11.25 msec page scan window */
2195
2196 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
2197 sizeof(acp), &acp);
2198 if (err < 0) {
2199 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002200 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002201 goto done;
2202 }
2203
2204 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
2205 if (err < 0) {
2206 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002207 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002208 goto done;
2209 }
2210
2211 err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
2212 NULL, 0);
2213done:
2214 hci_dev_unlock(hdev);
2215 hci_dev_put(hdev);
2216
2217 return err;
2218}
2219
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002220static int load_long_term_keys(struct sock *sk, u16 index,
2221 void *cp_data, u16 len)
2222{
2223 struct hci_dev *hdev;
2224 struct mgmt_cp_load_long_term_keys *cp = cp_data;
2225 u16 key_count, expected_len;
2226 int i;
2227
2228 if (len < sizeof(*cp))
2229 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2230 EINVAL);
2231
2232 key_count = get_unaligned_le16(&cp->key_count);
2233
2234 expected_len = sizeof(*cp) + key_count *
2235 sizeof(struct mgmt_ltk_info);
2236 if (expected_len != len) {
2237 BT_ERR("load_keys: expected %u bytes, got %u bytes",
2238 len, expected_len);
2239 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2240 EINVAL);
2241 }
2242
2243 hdev = hci_dev_get(index);
2244 if (!hdev)
2245 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2246 ENODEV);
2247
2248 BT_DBG("hci%u key_count %u", index, key_count);
2249
2250 hci_dev_lock(hdev);
2251
2252 hci_smp_ltks_clear(hdev);
2253
2254 for (i = 0; i < key_count; i++) {
2255 struct mgmt_ltk_info *key = &cp->keys[i];
2256 u8 type;
2257
2258 if (key->master)
2259 type = HCI_SMP_LTK;
2260 else
2261 type = HCI_SMP_LTK_SLAVE;
2262
2263 hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
2264 type, 0, key->authenticated, key->val,
2265 key->enc_size, key->ediv, key->rand);
2266 }
2267
2268 hci_dev_unlock(hdev);
2269 hci_dev_put(hdev);
2270
2271 return 0;
2272}
2273
Johan Hedberg03811012010-12-08 00:21:06 +02002274int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
2275{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002276 void *buf;
2277 u8 *cp;
Johan Hedberg03811012010-12-08 00:21:06 +02002278 struct mgmt_hdr *hdr;
Szymon Janc4e51eae2011-02-25 19:05:48 +01002279 u16 opcode, index, len;
Johan Hedberg03811012010-12-08 00:21:06 +02002280 int err;
2281
2282 BT_DBG("got %zu bytes", msglen);
2283
2284 if (msglen < sizeof(*hdr))
2285 return -EINVAL;
2286
Gustavo F. Padovane63a15e2011-04-04 18:56:53 -03002287 buf = kmalloc(msglen, GFP_KERNEL);
Johan Hedberg03811012010-12-08 00:21:06 +02002288 if (!buf)
2289 return -ENOMEM;
2290
2291 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
2292 err = -EFAULT;
2293 goto done;
2294 }
2295
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002296 hdr = buf;
Johan Hedberg03811012010-12-08 00:21:06 +02002297 opcode = get_unaligned_le16(&hdr->opcode);
Szymon Janc4e51eae2011-02-25 19:05:48 +01002298 index = get_unaligned_le16(&hdr->index);
Johan Hedberg03811012010-12-08 00:21:06 +02002299 len = get_unaligned_le16(&hdr->len);
2300
2301 if (len != msglen - sizeof(*hdr)) {
2302 err = -EINVAL;
2303 goto done;
2304 }
2305
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002306 cp = buf + sizeof(*hdr);
2307
Johan Hedberg03811012010-12-08 00:21:06 +02002308 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +02002309 case MGMT_OP_READ_VERSION:
2310 err = read_version(sk);
2311 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +02002312 case MGMT_OP_READ_INDEX_LIST:
2313 err = read_index_list(sk);
2314 break;
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002315 case MGMT_OP_READ_INFO:
Szymon Janc4e51eae2011-02-25 19:05:48 +01002316 err = read_controller_info(sk, index);
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002317 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002318 case MGMT_OP_SET_POWERED:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002319 err = set_powered(sk, index, cp, len);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002320 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +02002321 case MGMT_OP_SET_DISCOVERABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002322 err = set_discoverable(sk, index, cp, len);
Johan Hedberg73f22f62010-12-29 16:00:25 +02002323 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002324 case MGMT_OP_SET_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002325 err = set_connectable(sk, index, cp, len);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002326 break;
Johan Hedbergf7c68692011-12-15 00:47:36 +02002327 case MGMT_OP_SET_FAST_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002328 err = set_fast_connectable(sk, index, cp, len);
Johan Hedbergf7c68692011-12-15 00:47:36 +02002329 break;
Johan Hedbergc542a062011-01-26 13:11:03 +02002330 case MGMT_OP_SET_PAIRABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002331 err = set_pairable(sk, index, cp, len);
Johan Hedbergc542a062011-01-26 13:11:03 +02002332 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002333 case MGMT_OP_ADD_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002334 err = add_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002335 break;
2336 case MGMT_OP_REMOVE_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002337 err = remove_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002338 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002339 case MGMT_OP_SET_DEV_CLASS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002340 err = set_dev_class(sk, index, cp, len);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002341 break;
Johan Hedberg86742e12011-11-07 23:13:38 +02002342 case MGMT_OP_LOAD_LINK_KEYS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002343 err = load_link_keys(sk, index, cp, len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02002344 break;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002345 case MGMT_OP_DISCONNECT:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002346 err = disconnect(sk, index, cp, len);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002347 break;
Johan Hedberg2784eb42011-01-21 13:56:35 +02002348 case MGMT_OP_GET_CONNECTIONS:
Szymon Janc8ce62842011-03-01 16:55:32 +01002349 err = get_connections(sk, index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02002350 break;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002351 case MGMT_OP_PIN_CODE_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002352 err = pin_code_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002353 break;
2354 case MGMT_OP_PIN_CODE_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002355 err = pin_code_neg_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002356 break;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002357 case MGMT_OP_SET_IO_CAPABILITY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002358 err = set_io_capability(sk, index, cp, len);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002359 break;
Johan Hedberge9a416b2011-02-19 12:05:56 -03002360 case MGMT_OP_PAIR_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002361 err = pair_device(sk, index, cp, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002362 break;
Johan Hedberg28424702012-02-02 04:02:29 +02002363 case MGMT_OP_CANCEL_PAIR_DEVICE:
2364 err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len);
2365 break;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002366 case MGMT_OP_UNPAIR_DEVICE:
2367 err = unpair_device(sk, index, cp, len);
2368 break;
Johan Hedberga5c29682011-02-19 12:05:57 -03002369 case MGMT_OP_USER_CONFIRM_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002370 err = user_confirm_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002371 break;
2372 case MGMT_OP_USER_CONFIRM_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002373 err = user_confirm_neg_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002374 break;
Brian Gix604086b2011-11-23 08:28:33 -08002375 case MGMT_OP_USER_PASSKEY_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002376 err = user_passkey_reply(sk, index, cp, len);
Brian Gix604086b2011-11-23 08:28:33 -08002377 break;
2378 case MGMT_OP_USER_PASSKEY_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002379 err = user_passkey_neg_reply(sk, index, cp, len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02002380 break;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002381 case MGMT_OP_SET_LOCAL_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002382 err = set_local_name(sk, index, cp, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002383 break;
Szymon Jancc35938b2011-03-22 13:12:21 +01002384 case MGMT_OP_READ_LOCAL_OOB_DATA:
2385 err = read_local_oob_data(sk, index);
2386 break;
Szymon Janc2763eda2011-03-22 13:12:22 +01002387 case MGMT_OP_ADD_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002388 err = add_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002389 break;
2390 case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002391 err = remove_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002392 break;
Johan Hedberg14a53662011-04-27 10:29:56 -04002393 case MGMT_OP_START_DISCOVERY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002394 err = start_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002395 break;
2396 case MGMT_OP_STOP_DISCOVERY:
2397 err = stop_discovery(sk, index);
2398 break;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002399 case MGMT_OP_CONFIRM_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002400 err = confirm_name(sk, index, cp, len);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002401 break;
Antti Julku7fbec222011-06-15 12:01:15 +03002402 case MGMT_OP_BLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002403 err = block_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002404 break;
2405 case MGMT_OP_UNBLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002406 err = unblock_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002407 break;
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002408 case MGMT_OP_LOAD_LONG_TERM_KEYS:
2409 err = load_long_term_keys(sk, index, cp, len);
2410 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002411 default:
Johan Hedberg72a734e2010-12-30 00:38:22 +02002412 BT_DBG("Unknown op %u", opcode);
Johan Hedbergca69b792011-11-11 18:10:00 +02002413 err = cmd_status(sk, index, opcode,
2414 MGMT_STATUS_UNKNOWN_COMMAND);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002415 break;
2416 }
Johan Hedberg72a734e2010-12-30 00:38:22 +02002417
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002418 if (err < 0)
Johan Hedberg72a734e2010-12-30 00:38:22 +02002419 goto done;
2420
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002421 err = msglen;
2422
2423done:
2424 kfree(buf);
2425 return err;
2426}
2427
Johan Hedbergb24752f2011-11-03 14:40:33 +02002428static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
2429{
2430 u8 *status = data;
2431
2432 cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
2433 mgmt_pending_remove(cmd);
2434}
2435
Johan Hedberg744cf192011-11-08 20:40:14 +02002436int mgmt_index_added(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002437{
Johan Hedberg744cf192011-11-08 20:40:14 +02002438 return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002439}
2440
Johan Hedberg744cf192011-11-08 20:40:14 +02002441int mgmt_index_removed(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002442{
Johan Hedbergb24752f2011-11-03 14:40:33 +02002443 u8 status = ENODEV;
2444
Johan Hedberg744cf192011-11-08 20:40:14 +02002445 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002446
Johan Hedberg744cf192011-11-08 20:40:14 +02002447 return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002448}
2449
2450struct cmd_lookup {
2451 u8 val;
2452 struct sock *sk;
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002453 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +02002454};
2455
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002456static void settings_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberg03811012010-12-08 00:21:06 +02002457{
Johan Hedberg03811012010-12-08 00:21:06 +02002458 struct cmd_lookup *match = data;
2459
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002460 send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
Johan Hedberg03811012010-12-08 00:21:06 +02002461
2462 list_del(&cmd->list);
2463
2464 if (match->sk == NULL) {
2465 match->sk = cmd->sk;
2466 sock_hold(match->sk);
2467 }
2468
2469 mgmt_pending_free(cmd);
2470}
2471
Johan Hedberg744cf192011-11-08 20:40:14 +02002472int mgmt_powered(struct hci_dev *hdev, u8 powered)
Johan Hedberg03811012010-12-08 00:21:06 +02002473{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002474 struct cmd_lookup match = { powered, NULL, hdev };
2475 __le32 ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002476 int ret;
2477
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002478 mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002479
Johan Hedbergb24752f2011-11-03 14:40:33 +02002480 if (!powered) {
2481 u8 status = ENETDOWN;
Johan Hedberg744cf192011-11-08 20:40:14 +02002482 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002483 }
2484
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002485 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002486
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002487 ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
2488 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002489
2490 if (match.sk)
2491 sock_put(match.sk);
2492
2493 return ret;
2494}
2495
Johan Hedberg744cf192011-11-08 20:40:14 +02002496int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
Johan Hedberg03811012010-12-08 00:21:06 +02002497{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002498 struct cmd_lookup match = { discoverable, NULL, hdev };
2499 __le32 ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002500 int ret;
2501
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002502 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002503
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002504 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002505
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002506 ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002507 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002508 if (match.sk)
2509 sock_put(match.sk);
2510
2511 return ret;
2512}
2513
Johan Hedberg744cf192011-11-08 20:40:14 +02002514int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
Johan Hedberg03811012010-12-08 00:21:06 +02002515{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002516 __le32 ev;
2517 struct cmd_lookup match = { connectable, NULL, hdev };
Johan Hedberg03811012010-12-08 00:21:06 +02002518 int ret;
2519
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002520 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
2521 &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002522
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002523 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002524
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002525 ret = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002526
2527 if (match.sk)
2528 sock_put(match.sk);
2529
2530 return ret;
2531}
2532
Johan Hedberg744cf192011-11-08 20:40:14 +02002533int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002534{
Johan Hedbergca69b792011-11-11 18:10:00 +02002535 u8 mgmt_err = mgmt_status(status);
2536
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002537 if (scan & SCAN_PAGE)
Johan Hedberg744cf192011-11-08 20:40:14 +02002538 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002539 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002540
2541 if (scan & SCAN_INQUIRY)
Johan Hedberg744cf192011-11-08 20:40:14 +02002542 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002543 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002544
2545 return 0;
2546}
2547
Johan Hedberg744cf192011-11-08 20:40:14 +02002548int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
2549 u8 persistent)
Johan Hedberg03811012010-12-08 00:21:06 +02002550{
Johan Hedberg86742e12011-11-07 23:13:38 +02002551 struct mgmt_ev_new_link_key ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002552
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002553 memset(&ev, 0, sizeof(ev));
Johan Hedberg03811012010-12-08 00:21:06 +02002554
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002555 ev.store_hint = persistent;
2556 bacpy(&ev.key.bdaddr, &key->bdaddr);
2557 ev.key.type = key->type;
2558 memcpy(ev.key.val, key->val, 16);
2559 ev.key.pin_len = key->pin_len;
Johan Hedberg03811012010-12-08 00:21:06 +02002560
Johan Hedberg744cf192011-11-08 20:40:14 +02002561 return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002562}
Johan Hedbergf7520542011-01-20 12:34:39 +02002563
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002564int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
2565{
2566 struct mgmt_ev_new_long_term_key ev;
2567
2568 memset(&ev, 0, sizeof(ev));
2569
2570 ev.store_hint = persistent;
2571 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2572 ev.key.addr.type = key->bdaddr_type;
2573 ev.key.authenticated = key->authenticated;
2574 ev.key.enc_size = key->enc_size;
2575 ev.key.ediv = key->ediv;
2576
2577 if (key->type == HCI_SMP_LTK)
2578 ev.key.master = 1;
2579
2580 memcpy(ev.key.rand, key->rand, sizeof(key->rand));
2581 memcpy(ev.key.val, key->val, sizeof(key->val));
2582
2583 return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
2584 &ev, sizeof(ev), NULL);
2585}
2586
Johan Hedbergafc747a2012-01-15 18:11:07 +02002587int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedbergb644ba32012-01-17 21:48:47 +02002588 u8 addr_type, u8 *name, u8 name_len,
2589 u8 *dev_class)
Johan Hedbergf7520542011-01-20 12:34:39 +02002590{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002591 char buf[512];
2592 struct mgmt_ev_device_connected *ev = (void *) buf;
2593 u16 eir_len = 0;
Johan Hedbergf7520542011-01-20 12:34:39 +02002594
Johan Hedbergb644ba32012-01-17 21:48:47 +02002595 bacpy(&ev->addr.bdaddr, bdaddr);
2596 ev->addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002597
Johan Hedbergb644ba32012-01-17 21:48:47 +02002598 if (name_len > 0)
2599 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
2600 name, name_len);
2601
2602 if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
2603 eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
2604 EIR_CLASS_OF_DEV, dev_class, 3);
2605
2606 put_unaligned_le16(eir_len, &ev->eir_len);
2607
2608 return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
2609 sizeof(*ev) + eir_len, NULL);
Johan Hedbergf7520542011-01-20 12:34:39 +02002610}
2611
Johan Hedberg8962ee72011-01-20 12:40:27 +02002612static void disconnect_rsp(struct pending_cmd *cmd, void *data)
2613{
Szymon Jancc68fb7f2011-03-22 13:12:19 +01002614 struct mgmt_cp_disconnect *cp = cmd->param;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002615 struct sock **sk = data;
Johan Hedberga38528f2011-01-22 06:46:43 +02002616 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002617
Johan Hedberg88c3df12012-02-09 14:27:38 +02002618 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2619 rp.addr.type = cp->addr.type;
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002620 rp.status = 0;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002621
Szymon Janc4e51eae2011-02-25 19:05:48 +01002622 cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02002623
2624 *sk = cmd->sk;
2625 sock_hold(*sk);
2626
Johan Hedberga664b5b2011-02-19 12:06:02 -03002627 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002628}
2629
Johan Hedberg124f6e32012-02-09 13:50:12 +02002630static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberga8a1d192011-11-10 15:54:38 +02002631{
2632 u8 *status = data;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002633 struct mgmt_cp_unpair_device *cp = cmd->param;
2634 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002635
2636 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02002637 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2638 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002639 if (status != NULL)
2640 rp.status = *status;
2641
Johan Hedberg124f6e32012-02-09 13:50:12 +02002642 cmd_complete(cmd->sk, cmd->index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02002643 sizeof(rp));
2644
2645 mgmt_pending_remove(cmd);
2646}
2647
Johan Hedbergafc747a2012-01-15 18:11:07 +02002648int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
2649 u8 link_type, u8 addr_type)
Johan Hedbergf7520542011-01-20 12:34:39 +02002650{
Johan Hedberg4c659c32011-11-07 23:13:39 +02002651 struct mgmt_addr_info ev;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002652 struct sock *sk = NULL;
2653 int err;
2654
Johan Hedberg744cf192011-11-08 20:40:14 +02002655 mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
Johan Hedbergf7520542011-01-20 12:34:39 +02002656
Johan Hedbergf7520542011-01-20 12:34:39 +02002657 bacpy(&ev.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02002658 ev.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002659
Johan Hedbergafc747a2012-01-15 18:11:07 +02002660 err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
2661 sk);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002662
2663 if (sk)
2664 sock_put(sk);
2665
Johan Hedberg124f6e32012-02-09 13:50:12 +02002666 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
2667 NULL);
Johan Hedberga8a1d192011-11-10 15:54:38 +02002668
Johan Hedberg8962ee72011-01-20 12:40:27 +02002669 return err;
2670}
2671
Johan Hedberg88c3df12012-02-09 14:27:38 +02002672int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
2673 u8 link_type, u8 addr_type, u8 status)
Johan Hedberg8962ee72011-01-20 12:40:27 +02002674{
Johan Hedberg88c3df12012-02-09 14:27:38 +02002675 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002676 struct pending_cmd *cmd;
2677 int err;
2678
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002679 cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002680 if (!cmd)
2681 return -ENOENT;
2682
Johan Hedberg88c3df12012-02-09 14:27:38 +02002683 bacpy(&rp.addr.bdaddr, bdaddr);
2684 rp.addr.type = link_to_mgmt(link_type, addr_type);
2685 rp.status = mgmt_status(status);
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002686
Johan Hedberg88c3df12012-02-09 14:27:38 +02002687 err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002688 &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02002689
Johan Hedberga664b5b2011-02-19 12:06:02 -03002690 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002691
2692 return err;
Johan Hedbergf7520542011-01-20 12:34:39 +02002693}
Johan Hedberg17d5c042011-01-22 06:09:08 +02002694
Johan Hedberg48264f02011-11-09 13:58:58 +02002695int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
2696 u8 addr_type, u8 status)
Johan Hedberg17d5c042011-01-22 06:09:08 +02002697{
2698 struct mgmt_ev_connect_failed ev;
2699
Johan Hedberg4c659c32011-11-07 23:13:39 +02002700 bacpy(&ev.addr.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02002701 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02002702 ev.status = mgmt_status(status);
Johan Hedberg17d5c042011-01-22 06:09:08 +02002703
Johan Hedberg744cf192011-11-08 20:40:14 +02002704 return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg17d5c042011-01-22 06:09:08 +02002705}
Johan Hedberg980e1a52011-01-22 06:10:07 +02002706
Johan Hedberg744cf192011-11-08 20:40:14 +02002707int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
Johan Hedberg980e1a52011-01-22 06:10:07 +02002708{
2709 struct mgmt_ev_pin_code_request ev;
2710
Johan Hedberg980e1a52011-01-22 06:10:07 +02002711 bacpy(&ev.bdaddr, bdaddr);
Waldemar Rymarkiewicza770bb52011-04-28 12:07:59 +02002712 ev.secure = secure;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002713
Johan Hedberg744cf192011-11-08 20:40:14 +02002714 return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002715 NULL);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002716}
2717
Johan Hedberg744cf192011-11-08 20:40:14 +02002718int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2719 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02002720{
2721 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03002722 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002723 int err;
2724
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002725 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002726 if (!cmd)
2727 return -ENOENT;
2728
Johan Hedbergac56fb12011-02-19 12:05:59 -03002729 bacpy(&rp.bdaddr, bdaddr);
Johan Hedbergca69b792011-11-11 18:10:00 +02002730 rp.status = mgmt_status(status);
Johan Hedbergac56fb12011-02-19 12:05:59 -03002731
Johan Hedberg744cf192011-11-08 20:40:14 +02002732 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +01002733 sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02002734
Johan Hedberga664b5b2011-02-19 12:06:02 -03002735 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002736
2737 return err;
2738}
2739
Johan Hedberg744cf192011-11-08 20:40:14 +02002740int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2741 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02002742{
2743 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03002744 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002745 int err;
2746
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002747 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002748 if (!cmd)
2749 return -ENOENT;
2750
Johan Hedbergac56fb12011-02-19 12:05:59 -03002751 bacpy(&rp.bdaddr, bdaddr);
Johan Hedbergca69b792011-11-11 18:10:00 +02002752 rp.status = mgmt_status(status);
Johan Hedbergac56fb12011-02-19 12:05:59 -03002753
Johan Hedberg744cf192011-11-08 20:40:14 +02002754 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +01002755 sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02002756
Johan Hedberga664b5b2011-02-19 12:06:02 -03002757 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002758
2759 return err;
2760}
Johan Hedberga5c29682011-02-19 12:05:57 -03002761
Johan Hedberg744cf192011-11-08 20:40:14 +02002762int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002763 u8 link_type, u8 addr_type, __le32 value,
2764 u8 confirm_hint)
Johan Hedberga5c29682011-02-19 12:05:57 -03002765{
2766 struct mgmt_ev_user_confirm_request ev;
2767
Johan Hedberg744cf192011-11-08 20:40:14 +02002768 BT_DBG("%s", hdev->name);
Johan Hedberga5c29682011-02-19 12:05:57 -03002769
Johan Hedberg272d90d2012-02-09 15:26:12 +02002770 bacpy(&ev.addr.bdaddr, bdaddr);
2771 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg55bc1a32011-04-28 11:28:56 -07002772 ev.confirm_hint = confirm_hint;
Johan Hedberga5c29682011-02-19 12:05:57 -03002773 put_unaligned_le32(value, &ev.value);
2774
Johan Hedberg744cf192011-11-08 20:40:14 +02002775 return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002776 NULL);
Johan Hedberga5c29682011-02-19 12:05:57 -03002777}
2778
Johan Hedberg272d90d2012-02-09 15:26:12 +02002779int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
2780 u8 link_type, u8 addr_type)
Brian Gix604086b2011-11-23 08:28:33 -08002781{
2782 struct mgmt_ev_user_passkey_request ev;
2783
2784 BT_DBG("%s", hdev->name);
2785
Johan Hedberg272d90d2012-02-09 15:26:12 +02002786 bacpy(&ev.addr.bdaddr, bdaddr);
2787 ev.addr.type = link_to_mgmt(link_type, addr_type);
Brian Gix604086b2011-11-23 08:28:33 -08002788
2789 return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
2790 NULL);
2791}
2792
Brian Gix0df4c182011-11-16 13:53:13 -08002793static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002794 u8 link_type, u8 addr_type, u8 status,
2795 u8 opcode)
Johan Hedberga5c29682011-02-19 12:05:57 -03002796{
2797 struct pending_cmd *cmd;
2798 struct mgmt_rp_user_confirm_reply rp;
2799 int err;
2800
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002801 cmd = mgmt_pending_find(opcode, hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03002802 if (!cmd)
2803 return -ENOENT;
2804
Johan Hedberg272d90d2012-02-09 15:26:12 +02002805 bacpy(&rp.addr.bdaddr, bdaddr);
2806 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02002807 rp.status = mgmt_status(status);
Johan Hedberg744cf192011-11-08 20:40:14 +02002808 err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
Johan Hedberga5c29682011-02-19 12:05:57 -03002809
Johan Hedberga664b5b2011-02-19 12:06:02 -03002810 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03002811
2812 return err;
2813}
2814
Johan Hedberg744cf192011-11-08 20:40:14 +02002815int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002816 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03002817{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002818 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2819 status, MGMT_OP_USER_CONFIRM_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03002820}
2821
Johan Hedberg272d90d2012-02-09 15:26:12 +02002822int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2823 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03002824{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002825 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2826 status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03002827}
Johan Hedberg2a611692011-02-19 12:06:00 -03002828
Brian Gix604086b2011-11-23 08:28:33 -08002829int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002830 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08002831{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002832 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2833 status, MGMT_OP_USER_PASSKEY_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08002834}
2835
Johan Hedberg272d90d2012-02-09 15:26:12 +02002836int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
2837 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08002838{
Johan Hedberg272d90d2012-02-09 15:26:12 +02002839 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
2840 status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08002841}
2842
Johan Hedbergbab73cb2012-02-09 16:07:29 +02002843int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
2844 u8 addr_type, u8 status)
Johan Hedberg2a611692011-02-19 12:06:00 -03002845{
2846 struct mgmt_ev_auth_failed ev;
2847
Johan Hedbergbab73cb2012-02-09 16:07:29 +02002848 bacpy(&ev.addr.bdaddr, bdaddr);
2849 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02002850 ev.status = mgmt_status(status);
Johan Hedberg2a611692011-02-19 12:06:00 -03002851
Johan Hedberg744cf192011-11-08 20:40:14 +02002852 return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg2a611692011-02-19 12:06:00 -03002853}
Johan Hedbergb312b1612011-03-16 14:29:37 +02002854
Johan Hedberg744cf192011-11-08 20:40:14 +02002855int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
Johan Hedbergb312b1612011-03-16 14:29:37 +02002856{
2857 struct pending_cmd *cmd;
2858 struct mgmt_cp_set_local_name ev;
2859 int err;
2860
2861 memset(&ev, 0, sizeof(ev));
2862 memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
2863
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002864 cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002865 if (!cmd)
2866 goto send_event;
2867
2868 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02002869 err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02002870 mgmt_status(status));
Johan Hedbergb312b1612011-03-16 14:29:37 +02002871 goto failed;
2872 }
2873
Johan Hedberg744cf192011-11-08 20:40:14 +02002874 update_eir(hdev);
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03002875
Johan Hedberg744cf192011-11-08 20:40:14 +02002876 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,
Johan Hedbergb312b1612011-03-16 14:29:37 +02002877 sizeof(ev));
2878 if (err < 0)
2879 goto failed;
2880
2881send_event:
Johan Hedberg744cf192011-11-08 20:40:14 +02002882 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
Johan Hedbergb312b1612011-03-16 14:29:37 +02002883 cmd ? cmd->sk : NULL);
2884
2885failed:
2886 if (cmd)
2887 mgmt_pending_remove(cmd);
2888 return err;
2889}
Szymon Jancc35938b2011-03-22 13:12:21 +01002890
Johan Hedberg744cf192011-11-08 20:40:14 +02002891int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
2892 u8 *randomizer, u8 status)
Szymon Jancc35938b2011-03-22 13:12:21 +01002893{
2894 struct pending_cmd *cmd;
2895 int err;
2896
Johan Hedberg744cf192011-11-08 20:40:14 +02002897 BT_DBG("%s status %u", hdev->name, status);
Szymon Jancc35938b2011-03-22 13:12:21 +01002898
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002899 cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002900 if (!cmd)
2901 return -ENOENT;
2902
2903 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02002904 err = cmd_status(cmd->sk, hdev->id,
Johan Hedbergca69b792011-11-11 18:10:00 +02002905 MGMT_OP_READ_LOCAL_OOB_DATA,
2906 mgmt_status(status));
Szymon Jancc35938b2011-03-22 13:12:21 +01002907 } else {
2908 struct mgmt_rp_read_local_oob_data rp;
2909
2910 memcpy(rp.hash, hash, sizeof(rp.hash));
2911 memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
2912
Johan Hedberg744cf192011-11-08 20:40:14 +02002913 err = cmd_complete(cmd->sk, hdev->id,
2914 MGMT_OP_READ_LOCAL_OOB_DATA,
2915 &rp, sizeof(rp));
Szymon Jancc35938b2011-03-22 13:12:21 +01002916 }
2917
2918 mgmt_pending_remove(cmd);
2919
2920 return err;
2921}
Johan Hedberge17acd42011-03-30 23:57:16 +03002922
Johan Hedberg48264f02011-11-09 13:58:58 +02002923int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg561aafb2012-01-04 13:31:59 +02002924 u8 addr_type, u8 *dev_class, s8 rssi,
Johan Hedberge319d2e2012-01-15 19:51:59 +02002925 u8 cfm_name, u8 *eir, u16 eir_len)
Johan Hedberge17acd42011-03-30 23:57:16 +03002926{
Johan Hedberge319d2e2012-01-15 19:51:59 +02002927 char buf[512];
2928 struct mgmt_ev_device_found *ev = (void *) buf;
Johan Hedberg1dc06092012-01-15 21:01:23 +02002929 size_t ev_size;
Johan Hedberge17acd42011-03-30 23:57:16 +03002930
Johan Hedberg1dc06092012-01-15 21:01:23 +02002931 /* Leave 5 bytes for a potential CoD field */
2932 if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
Andre Guedes7d262f82012-01-10 18:20:49 -03002933 return -EINVAL;
2934
Johan Hedberg1dc06092012-01-15 21:01:23 +02002935 memset(buf, 0, sizeof(buf));
2936
Johan Hedberge319d2e2012-01-15 19:51:59 +02002937 bacpy(&ev->addr.bdaddr, bdaddr);
2938 ev->addr.type = link_to_mgmt(link_type, addr_type);
2939 ev->rssi = rssi;
2940 ev->confirm_name = cfm_name;
Johan Hedberge17acd42011-03-30 23:57:16 +03002941
Johan Hedberg1dc06092012-01-15 21:01:23 +02002942 if (eir_len > 0)
Johan Hedberge319d2e2012-01-15 19:51:59 +02002943 memcpy(ev->eir, eir, eir_len);
Johan Hedberge17acd42011-03-30 23:57:16 +03002944
Johan Hedberg1dc06092012-01-15 21:01:23 +02002945 if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
2946 eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
2947 dev_class, 3);
2948
2949 put_unaligned_le16(eir_len, &ev->eir_len);
2950
2951 ev_size = sizeof(*ev) + eir_len;
Andre Guedesf8523592011-09-09 18:56:26 -03002952
Johan Hedberge319d2e2012-01-15 19:51:59 +02002953 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
Johan Hedberge17acd42011-03-30 23:57:16 +03002954}
Johan Hedberga88a9652011-03-30 13:18:12 +03002955
Johan Hedbergb644ba32012-01-17 21:48:47 +02002956int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
2957 u8 addr_type, s8 rssi, u8 *name, u8 name_len)
Johan Hedberga88a9652011-03-30 13:18:12 +03002958{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002959 struct mgmt_ev_device_found *ev;
2960 char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
2961 u16 eir_len;
Johan Hedberga88a9652011-03-30 13:18:12 +03002962
Johan Hedbergb644ba32012-01-17 21:48:47 +02002963 ev = (struct mgmt_ev_device_found *) buf;
Johan Hedberga88a9652011-03-30 13:18:12 +03002964
Johan Hedbergb644ba32012-01-17 21:48:47 +02002965 memset(buf, 0, sizeof(buf));
Johan Hedberga88a9652011-03-30 13:18:12 +03002966
Johan Hedbergb644ba32012-01-17 21:48:47 +02002967 bacpy(&ev->addr.bdaddr, bdaddr);
2968 ev->addr.type = link_to_mgmt(link_type, addr_type);
2969 ev->rssi = rssi;
2970
2971 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
2972 name_len);
2973
2974 put_unaligned_le16(eir_len, &ev->eir_len);
2975
Johan Hedberg053c7e02012-02-04 00:06:00 +02002976 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
2977 sizeof(*ev) + eir_len, NULL);
Johan Hedberga88a9652011-03-30 13:18:12 +03002978}
Johan Hedberg314b2382011-04-27 10:29:57 -04002979
Andre Guedes7a135102011-11-09 17:14:25 -03002980int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
Johan Hedberg164a6e72011-11-01 17:06:44 +02002981{
2982 struct pending_cmd *cmd;
2983 int err;
2984
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002985 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02002986 if (!cmd)
2987 return -ENOENT;
2988
Johan Hedbergca69b792011-11-11 18:10:00 +02002989 err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
Johan Hedberg164a6e72011-11-01 17:06:44 +02002990 mgmt_pending_remove(cmd);
2991
2992 return err;
2993}
2994
Andre Guedese6d465c2011-11-09 17:14:26 -03002995int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
2996{
2997 struct pending_cmd *cmd;
2998 int err;
2999
3000 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
3001 if (!cmd)
3002 return -ENOENT;
3003
Andre Guedese75a8b0c2012-01-02 16:50:53 -03003004 err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
Johan Hedberg03811012010-12-08 00:21:06 +02003005 mgmt_pending_remove(cmd);
3006
3007 return err;
3008}
Johan Hedberg314b2382011-04-27 10:29:57 -04003009
Johan Hedberg744cf192011-11-08 20:40:14 +02003010int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
Johan Hedberg314b2382011-04-27 10:29:57 -04003011{
Johan Hedberg164a6e72011-11-01 17:06:44 +02003012 struct pending_cmd *cmd;
3013
Andre Guedes343fb142011-11-22 17:14:19 -03003014 BT_DBG("%s discovering %u", hdev->name, discovering);
3015
Johan Hedberg164a6e72011-11-01 17:06:44 +02003016 if (discovering)
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003017 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003018 else
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003019 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003020
3021 if (cmd != NULL) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003022 cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003023 mgmt_pending_remove(cmd);
3024 }
3025
Johan Hedberg744cf192011-11-08 20:40:14 +02003026 return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,
Johan Hedberg314b2382011-04-27 10:29:57 -04003027 sizeof(discovering), NULL);
3028}
Antti Julku5e762442011-08-25 16:48:02 +03003029
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003030int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003031{
3032 struct pending_cmd *cmd;
3033 struct mgmt_ev_device_blocked ev;
3034
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003035 cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003036
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003037 bacpy(&ev.addr.bdaddr, bdaddr);
3038 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003039
Johan Hedberg744cf192011-11-08 20:40:14 +02003040 return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
3041 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003042}
3043
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003044int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003045{
3046 struct pending_cmd *cmd;
3047 struct mgmt_ev_device_unblocked ev;
3048
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003049 cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003050
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003051 bacpy(&ev.addr.bdaddr, bdaddr);
3052 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003053
Johan Hedberg744cf192011-11-08 20:40:14 +02003054 return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
3055 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003056}