blob: cc9fb64def901d5e8b793cdac6891bcd20d6c70f [file] [log] [blame]
Johan Hedberg03811012010-12-08 00:21:06 +02001/*
2 BlueZ - Bluetooth protocol stack for Linux
Johan Hedbergea585ab2012-02-17 14:50:39 +02003
Johan Hedberg03811012010-12-08 00:21:06 +02004 Copyright (C) 2010 Nokia Corporation
Johan Hedbergea585ab2012-02-17 14:50:39 +02005 Copyright (C) 2011-2012 Intel Corporation
Johan Hedberg03811012010-12-08 00:21:06 +02006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License version 2 as
9 published by the Free Software Foundation;
10
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
12 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
14 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
15 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
16 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19
20 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
21 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
22 SOFTWARE IS DISCLAIMED.
23*/
24
25/* Bluetooth HCI Management interface */
26
Johan Hedbergca69b792011-11-11 18:10:00 +020027#include <linux/kernel.h>
Szymon Janc72359752011-02-17 14:16:32 +010028#include <linux/uaccess.h>
Paul Gortmaker3a9a2312011-05-27 09:12:25 -040029#include <linux/module.h>
Johan Hedberg03811012010-12-08 00:21:06 +020030#include <asm/unaligned.h>
31
32#include <net/bluetooth/bluetooth.h>
33#include <net/bluetooth/hci_core.h>
34#include <net/bluetooth/mgmt.h>
Brian Gix5fe57d92011-12-21 16:12:13 -080035#include <net/bluetooth/smp.h>
Johan Hedberg03811012010-12-08 00:21:06 +020036
Marcel Holtmannd7b7e792012-02-20 21:47:49 +010037bool enable_hs;
38bool enable_le;
39
Johan Hedberg2da9c552012-02-17 14:39:28 +020040#define MGMT_VERSION 1
41#define MGMT_REVISION 0
Johan Hedberg02d98122010-12-13 21:07:04 +020042
Johan Hedberge70bb2e2012-02-13 16:59:33 +020043static const u16 mgmt_commands[] = {
44 MGMT_OP_READ_INDEX_LIST,
45 MGMT_OP_READ_INFO,
46 MGMT_OP_SET_POWERED,
47 MGMT_OP_SET_DISCOVERABLE,
48 MGMT_OP_SET_CONNECTABLE,
49 MGMT_OP_SET_FAST_CONNECTABLE,
50 MGMT_OP_SET_PAIRABLE,
51 MGMT_OP_SET_LINK_SECURITY,
52 MGMT_OP_SET_SSP,
53 MGMT_OP_SET_HS,
54 MGMT_OP_SET_LE,
55 MGMT_OP_SET_DEV_CLASS,
56 MGMT_OP_SET_LOCAL_NAME,
57 MGMT_OP_ADD_UUID,
58 MGMT_OP_REMOVE_UUID,
59 MGMT_OP_LOAD_LINK_KEYS,
60 MGMT_OP_LOAD_LONG_TERM_KEYS,
61 MGMT_OP_DISCONNECT,
62 MGMT_OP_GET_CONNECTIONS,
63 MGMT_OP_PIN_CODE_REPLY,
64 MGMT_OP_PIN_CODE_NEG_REPLY,
65 MGMT_OP_SET_IO_CAPABILITY,
66 MGMT_OP_PAIR_DEVICE,
67 MGMT_OP_CANCEL_PAIR_DEVICE,
68 MGMT_OP_UNPAIR_DEVICE,
69 MGMT_OP_USER_CONFIRM_REPLY,
70 MGMT_OP_USER_CONFIRM_NEG_REPLY,
71 MGMT_OP_USER_PASSKEY_REPLY,
72 MGMT_OP_USER_PASSKEY_NEG_REPLY,
73 MGMT_OP_READ_LOCAL_OOB_DATA,
74 MGMT_OP_ADD_REMOTE_OOB_DATA,
75 MGMT_OP_REMOVE_REMOTE_OOB_DATA,
76 MGMT_OP_START_DISCOVERY,
77 MGMT_OP_STOP_DISCOVERY,
78 MGMT_OP_CONFIRM_NAME,
79 MGMT_OP_BLOCK_DEVICE,
80 MGMT_OP_UNBLOCK_DEVICE,
81};
82
83static const u16 mgmt_events[] = {
84 MGMT_EV_CONTROLLER_ERROR,
85 MGMT_EV_INDEX_ADDED,
86 MGMT_EV_INDEX_REMOVED,
87 MGMT_EV_NEW_SETTINGS,
88 MGMT_EV_CLASS_OF_DEV_CHANGED,
89 MGMT_EV_LOCAL_NAME_CHANGED,
90 MGMT_EV_NEW_LINK_KEY,
91 MGMT_EV_NEW_LONG_TERM_KEY,
92 MGMT_EV_DEVICE_CONNECTED,
93 MGMT_EV_DEVICE_DISCONNECTED,
94 MGMT_EV_CONNECT_FAILED,
95 MGMT_EV_PIN_CODE_REQUEST,
96 MGMT_EV_USER_CONFIRM_REQUEST,
97 MGMT_EV_USER_PASSKEY_REQUEST,
98 MGMT_EV_AUTH_FAILED,
99 MGMT_EV_DEVICE_FOUND,
100 MGMT_EV_DISCOVERING,
101 MGMT_EV_DEVICE_BLOCKED,
102 MGMT_EV_DEVICE_UNBLOCKED,
103 MGMT_EV_DEVICE_UNPAIRED,
104};
105
Andre Guedes3fd24152012-02-03 17:48:01 -0300106/*
107 * These LE scan and inquiry parameters were chosen according to LE General
108 * Discovery Procedure specification.
109 */
110#define LE_SCAN_TYPE 0x01
111#define LE_SCAN_WIN 0x12
112#define LE_SCAN_INT 0x12
113#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */
Andre Guedes5e0452c2012-02-17 20:39:38 -0300114#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */
Andre Guedes3fd24152012-02-03 17:48:01 -0300115
Andre Guedese8777522012-02-03 17:48:02 -0300116#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
Andre Guedes5e0452c2012-02-17 20:39:38 -0300117#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */
Andre Guedes2519a1f2011-11-07 11:45:24 -0300118
Johan Hedberg7d785252011-12-15 00:47:39 +0200119#define SERVICE_CACHE_TIMEOUT (5 * 1000)
120
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200121#define hdev_is_powered(hdev) (test_bit(HCI_UP, &hdev->flags) && \
122 !test_bit(HCI_AUTO_OFF, &hdev->dev_flags))
123
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200124struct pending_cmd {
125 struct list_head list;
Johan Hedbergfc2f4b12011-11-09 13:58:56 +0200126 u16 opcode;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200127 int index;
Szymon Jancc68fb7f2011-03-22 13:12:19 +0100128 void *param;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200129 struct sock *sk;
Johan Hedberge9a416b2011-02-19 12:05:56 -0300130 void *user_data;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200131};
132
Johan Hedbergca69b792011-11-11 18:10:00 +0200133/* HCI to MGMT error code conversion table */
134static u8 mgmt_status_table[] = {
135 MGMT_STATUS_SUCCESS,
136 MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */
137 MGMT_STATUS_NOT_CONNECTED, /* No Connection */
138 MGMT_STATUS_FAILED, /* Hardware Failure */
139 MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */
140 MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */
141 MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */
142 MGMT_STATUS_NO_RESOURCES, /* Memory Full */
143 MGMT_STATUS_TIMEOUT, /* Connection Timeout */
144 MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */
145 MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */
146 MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */
147 MGMT_STATUS_BUSY, /* Command Disallowed */
148 MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */
149 MGMT_STATUS_REJECTED, /* Rejected Security */
150 MGMT_STATUS_REJECTED, /* Rejected Personal */
151 MGMT_STATUS_TIMEOUT, /* Host Timeout */
152 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */
153 MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */
154 MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */
155 MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */
156 MGMT_STATUS_DISCONNECTED, /* OE Power Off */
157 MGMT_STATUS_DISCONNECTED, /* Connection Terminated */
158 MGMT_STATUS_BUSY, /* Repeated Attempts */
159 MGMT_STATUS_REJECTED, /* Pairing Not Allowed */
160 MGMT_STATUS_FAILED, /* Unknown LMP PDU */
161 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */
162 MGMT_STATUS_REJECTED, /* SCO Offset Rejected */
163 MGMT_STATUS_REJECTED, /* SCO Interval Rejected */
164 MGMT_STATUS_REJECTED, /* Air Mode Rejected */
165 MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */
166 MGMT_STATUS_FAILED, /* Unspecified Error */
167 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */
168 MGMT_STATUS_FAILED, /* Role Change Not Allowed */
169 MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */
170 MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */
171 MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */
172 MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */
173 MGMT_STATUS_FAILED, /* Unit Link Key Used */
174 MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */
175 MGMT_STATUS_TIMEOUT, /* Instant Passed */
176 MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */
177 MGMT_STATUS_FAILED, /* Transaction Collision */
178 MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */
179 MGMT_STATUS_REJECTED, /* QoS Rejected */
180 MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */
181 MGMT_STATUS_REJECTED, /* Insufficient Security */
182 MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */
183 MGMT_STATUS_BUSY, /* Role Switch Pending */
184 MGMT_STATUS_FAILED, /* Slot Violation */
185 MGMT_STATUS_FAILED, /* Role Switch Failed */
186 MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */
187 MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */
188 MGMT_STATUS_BUSY, /* Host Busy Pairing */
189 MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */
190 MGMT_STATUS_BUSY, /* Controller Busy */
191 MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */
192 MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */
193 MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */
194 MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */
195 MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */
196};
197
198static u8 mgmt_status(u8 hci_status)
199{
200 if (hci_status < ARRAY_SIZE(mgmt_status_table))
201 return mgmt_status_table[hci_status];
202
203 return MGMT_STATUS_FAILED;
204}
205
Szymon Janc4e51eae2011-02-25 19:05:48 +0100206static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200207{
208 struct sk_buff *skb;
209 struct mgmt_hdr *hdr;
210 struct mgmt_ev_cmd_status *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300211 int err;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200212
Szymon Janc34eb5252011-02-28 14:10:08 +0100213 BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200214
215 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
216 if (!skb)
217 return -ENOMEM;
218
219 hdr = (void *) skb_put(skb, sizeof(*hdr));
220
221 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100222 hdr->index = cpu_to_le16(index);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200223 hdr->len = cpu_to_le16(sizeof(*ev));
224
225 ev = (void *) skb_put(skb, sizeof(*ev));
226 ev->status = status;
227 put_unaligned_le16(cmd, &ev->opcode);
228
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300229 err = sock_queue_rcv_skb(sk, skb);
230 if (err < 0)
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200231 kfree_skb(skb);
232
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300233 return err;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200234}
235
Johan Hedbergaee9b212012-02-18 15:07:59 +0200236static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
237 void *rp, size_t rp_len)
Johan Hedberg02d98122010-12-13 21:07:04 +0200238{
239 struct sk_buff *skb;
240 struct mgmt_hdr *hdr;
241 struct mgmt_ev_cmd_complete *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300242 int err;
Johan Hedberg02d98122010-12-13 21:07:04 +0200243
244 BT_DBG("sock %p", sk);
245
Johan Hedberga38528f2011-01-22 06:46:43 +0200246 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC);
Johan Hedberg02d98122010-12-13 21:07:04 +0200247 if (!skb)
248 return -ENOMEM;
249
250 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedberga38528f2011-01-22 06:46:43 +0200251
Johan Hedberg02d98122010-12-13 21:07:04 +0200252 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100253 hdr->index = cpu_to_le16(index);
Johan Hedberga38528f2011-01-22 06:46:43 +0200254 hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200255
Johan Hedberga38528f2011-01-22 06:46:43 +0200256 ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
257 put_unaligned_le16(cmd, &ev->opcode);
Johan Hedbergaee9b212012-02-18 15:07:59 +0200258 ev->status = status;
Szymon Janc8020c162011-02-28 14:09:50 +0100259
260 if (rp)
261 memcpy(ev->data, rp, rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200262
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300263 err = sock_queue_rcv_skb(sk, skb);
264 if (err < 0)
Johan Hedberg02d98122010-12-13 21:07:04 +0200265 kfree_skb(skb);
266
Marcel Holtmanne5f0e152012-02-22 11:59:01 +0100267 return err;
Johan Hedberg02d98122010-12-13 21:07:04 +0200268}
269
Johan Hedberga38528f2011-01-22 06:46:43 +0200270static int read_version(struct sock *sk)
271{
272 struct mgmt_rp_read_version rp;
273
274 BT_DBG("sock %p", sk);
275
276 rp.version = MGMT_VERSION;
277 put_unaligned_le16(MGMT_REVISION, &rp.revision);
278
Johan Hedbergaee9b212012-02-18 15:07:59 +0200279 return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +0100280 sizeof(rp));
Johan Hedberga38528f2011-01-22 06:46:43 +0200281}
282
Johan Hedberge70bb2e2012-02-13 16:59:33 +0200283static int read_commands(struct sock *sk)
284{
285 struct mgmt_rp_read_commands *rp;
286 u16 num_commands = ARRAY_SIZE(mgmt_commands);
287 u16 num_events = ARRAY_SIZE(mgmt_events);
288 u16 *opcode;
289 size_t rp_size;
290 int i, err;
291
292 BT_DBG("sock %p", sk);
293
294 rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16));
295
296 rp = kmalloc(rp_size, GFP_KERNEL);
297 if (!rp)
298 return -ENOMEM;
299
300 put_unaligned_le16(num_commands, &rp->num_commands);
301 put_unaligned_le16(num_events, &rp->num_events);
302
303 for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++)
304 put_unaligned_le16(mgmt_commands[i], opcode);
305
306 for (i = 0; i < num_events; i++, opcode++)
307 put_unaligned_le16(mgmt_events[i], opcode);
308
Johan Hedbergaee9b212012-02-18 15:07:59 +0200309 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, rp,
Johan Hedberge70bb2e2012-02-13 16:59:33 +0200310 rp_size);
311 kfree(rp);
312
313 return err;
314}
315
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200316static int read_index_list(struct sock *sk)
317{
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200318 struct mgmt_rp_read_index_list *rp;
319 struct list_head *p;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200320 struct hci_dev *d;
Johan Hedberga38528f2011-01-22 06:46:43 +0200321 size_t rp_len;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200322 u16 count;
Johan Hedberga38528f2011-01-22 06:46:43 +0200323 int i, err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200324
325 BT_DBG("sock %p", sk);
326
327 read_lock(&hci_dev_list_lock);
328
329 count = 0;
330 list_for_each(p, &hci_dev_list) {
331 count++;
332 }
333
Johan Hedberga38528f2011-01-22 06:46:43 +0200334 rp_len = sizeof(*rp) + (2 * count);
335 rp = kmalloc(rp_len, GFP_ATOMIC);
336 if (!rp) {
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100337 read_unlock(&hci_dev_list_lock);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200338 return -ENOMEM;
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100339 }
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200340
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200341 put_unaligned_le16(count, &rp->num_controllers);
342
343 i = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200344 list_for_each_entry(d, &hci_dev_list, list) {
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200345 if (test_bit(HCI_SETUP, &d->dev_flags))
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200346 continue;
347
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200348 put_unaligned_le16(d->id, &rp->index[i++]);
349 BT_DBG("Added hci%u", d->id);
350 }
351
352 read_unlock(&hci_dev_list_lock);
353
Johan Hedbergaee9b212012-02-18 15:07:59 +0200354 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 0, rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +0100355 rp_len);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200356
Johan Hedberga38528f2011-01-22 06:46:43 +0200357 kfree(rp);
358
359 return err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200360}
361
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200362static u32 get_supported_settings(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +0200363{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200364 u32 settings = 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200365
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200366 settings |= MGMT_SETTING_POWERED;
367 settings |= MGMT_SETTING_CONNECTABLE;
368 settings |= MGMT_SETTING_FAST_CONNECTABLE;
369 settings |= MGMT_SETTING_DISCOVERABLE;
370 settings |= MGMT_SETTING_PAIRABLE;
Johan Hedberg03811012010-12-08 00:21:06 +0200371
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200372 if (hdev->features[6] & LMP_SIMPLE_PAIR)
373 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200374
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200375 if (!(hdev->features[4] & LMP_NO_BREDR)) {
376 settings |= MGMT_SETTING_BREDR;
377 settings |= MGMT_SETTING_LINK_SECURITY;
378 }
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200379
Marcel Holtmannd7b7e792012-02-20 21:47:49 +0100380 if (enable_hs)
381 settings |= MGMT_SETTING_HS;
382
383 if (enable_le) {
384 if (hdev->features[4] & LMP_LE)
385 settings |= MGMT_SETTING_LE;
386 }
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200387
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200388 return settings;
389}
Johan Hedbergebc99fe2011-01-04 11:54:26 +0200390
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200391static u32 get_current_settings(struct hci_dev *hdev)
392{
393 u32 settings = 0;
Johan Hedbergdc4fe302011-03-16 14:29:36 +0200394
Johan Hedbergf1f0eb02012-02-21 17:15:41 +0200395 if (hdev_is_powered(hdev))
Marcel Holtmannf0d4b782012-02-21 12:14:25 +0100396 settings |= MGMT_SETTING_POWERED;
397
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200398 if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200399 settings |= MGMT_SETTING_CONNECTABLE;
400
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200401 if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200402 settings |= MGMT_SETTING_DISCOVERABLE;
403
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200404 if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200405 settings |= MGMT_SETTING_PAIRABLE;
406
407 if (!(hdev->features[4] & LMP_NO_BREDR))
408 settings |= MGMT_SETTING_BREDR;
409
Johan Hedberg06199cf2012-02-22 16:37:11 +0200410 if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200411 settings |= MGMT_SETTING_LE;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200412
Johan Hedberg47990ea2012-02-22 11:58:37 +0200413 if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200414 settings |= MGMT_SETTING_LINK_SECURITY;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200415
Johan Hedberg84bde9d6c2012-01-25 14:21:06 +0200416 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200417 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200418
Johan Hedberg6d80dfd2012-02-20 23:50:38 +0200419 if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags))
420 settings |= MGMT_SETTING_HS;
421
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200422 return settings;
Johan Hedbergc542a062011-01-26 13:11:03 +0200423}
424
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300425#define PNP_INFO_SVCLASS_ID 0x1200
426
427static u8 bluetooth_base_uuid[] = {
428 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
429 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
430};
431
432static u16 get_uuid16(u8 *uuid128)
433{
434 u32 val;
435 int i;
436
437 for (i = 0; i < 12; i++) {
438 if (bluetooth_base_uuid[i] != uuid128[i])
439 return 0;
440 }
441
442 memcpy(&val, &uuid128[12], 4);
443
444 val = le32_to_cpu(val);
445 if (val > 0xffff)
446 return 0;
447
448 return (u16) val;
449}
450
451static void create_eir(struct hci_dev *hdev, u8 *data)
452{
453 u8 *ptr = data;
454 u16 eir_len = 0;
455 u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
456 int i, truncated = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200457 struct bt_uuid *uuid;
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300458 size_t name_len;
459
460 name_len = strlen(hdev->dev_name);
461
462 if (name_len > 0) {
463 /* EIR Data type */
464 if (name_len > 48) {
465 name_len = 48;
466 ptr[1] = EIR_NAME_SHORT;
467 } else
468 ptr[1] = EIR_NAME_COMPLETE;
469
470 /* EIR Data length */
471 ptr[0] = name_len + 1;
472
473 memcpy(ptr + 2, hdev->dev_name, name_len);
474
475 eir_len += (name_len + 2);
476 ptr += (name_len + 2);
477 }
478
479 memset(uuid16_list, 0, sizeof(uuid16_list));
480
481 /* Group all UUID16 types */
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200482 list_for_each_entry(uuid, &hdev->uuids, list) {
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300483 u16 uuid16;
484
485 uuid16 = get_uuid16(uuid->uuid);
486 if (uuid16 == 0)
487 return;
488
489 if (uuid16 < 0x1100)
490 continue;
491
492 if (uuid16 == PNP_INFO_SVCLASS_ID)
493 continue;
494
495 /* Stop if not enough space to put next UUID */
496 if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
497 truncated = 1;
498 break;
499 }
500
501 /* Check for duplicates */
502 for (i = 0; uuid16_list[i] != 0; i++)
503 if (uuid16_list[i] == uuid16)
504 break;
505
506 if (uuid16_list[i] == 0) {
507 uuid16_list[i] = uuid16;
508 eir_len += sizeof(u16);
509 }
510 }
511
512 if (uuid16_list[0] != 0) {
513 u8 *length = ptr;
514
515 /* EIR Data type */
516 ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
517
518 ptr += 2;
519 eir_len += 2;
520
521 for (i = 0; uuid16_list[i] != 0; i++) {
522 *ptr++ = (uuid16_list[i] & 0x00ff);
523 *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
524 }
525
526 /* EIR Data length */
527 *length = (i * sizeof(u16)) + 1;
528 }
529}
530
531static int update_eir(struct hci_dev *hdev)
532{
533 struct hci_cp_write_eir cp;
534
Johan Hedberg504c8dc2012-02-23 13:30:41 +0200535 if (!hdev_is_powered(hdev))
Johan Hedberg7770c4a2012-02-22 22:06:38 +0200536 return 0;
537
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300538 if (!(hdev->features[6] & LMP_EXT_INQ))
539 return 0;
540
Johan Hedberg84bde9d6c2012-01-25 14:21:06 +0200541 if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300542 return 0;
543
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200544 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300545 return 0;
546
547 memset(&cp, 0, sizeof(cp));
548
549 create_eir(hdev, cp.data);
550
551 if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
552 return 0;
553
554 memcpy(hdev->eir, cp.data, sizeof(cp.data));
555
556 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
557}
558
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200559static u8 get_service_classes(struct hci_dev *hdev)
560{
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300561 struct bt_uuid *uuid;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200562 u8 val = 0;
563
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300564 list_for_each_entry(uuid, &hdev->uuids, list)
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200565 val |= uuid->svc_hint;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200566
567 return val;
568}
569
570static int update_class(struct hci_dev *hdev)
571{
572 u8 cod[3];
Johan Hedbergc95f0ba2012-02-23 22:54:38 +0200573 int err;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200574
575 BT_DBG("%s", hdev->name);
576
Johan Hedberg504c8dc2012-02-23 13:30:41 +0200577 if (!hdev_is_powered(hdev))
Johan Hedberg7770c4a2012-02-22 22:06:38 +0200578 return 0;
579
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200580 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200581 return 0;
582
583 cod[0] = hdev->minor_class;
584 cod[1] = hdev->major_class;
585 cod[2] = get_service_classes(hdev);
586
587 if (memcmp(cod, hdev->dev_class, 3) == 0)
588 return 0;
589
Johan Hedbergc95f0ba2012-02-23 22:54:38 +0200590 err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
591 if (err == 0)
592 set_bit(HCI_PENDING_CLASS, &hdev->dev_flags);
593
594 return err;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200595}
596
Johan Hedberg7d785252011-12-15 00:47:39 +0200597static void service_cache_off(struct work_struct *work)
598{
599 struct hci_dev *hdev = container_of(work, struct hci_dev,
600 service_cache.work);
601
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200602 if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200603 return;
604
605 hci_dev_lock(hdev);
606
607 update_eir(hdev);
608 update_class(hdev);
609
610 hci_dev_unlock(hdev);
611}
612
613static void mgmt_init_hdev(struct hci_dev *hdev)
614{
Johan Hedberg0cbf4ed2012-02-21 17:25:22 +0200615 if (!test_and_set_bit(HCI_MGMT, &hdev->dev_flags)) {
Johan Hedberg7d785252011-12-15 00:47:39 +0200616 INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
617
Johan Hedberg0cbf4ed2012-02-21 17:25:22 +0200618 /* Non-mgmt controlled devices get this bit set
619 * implicitly so that pairing works for them, however
620 * for mgmt we require user-space to explicitly enable
621 * it
622 */
623 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
624 }
Johan Hedberg7d785252011-12-15 00:47:39 +0200625}
626
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200627static int read_controller_info(struct sock *sk, struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +0200628{
629 struct mgmt_rp_read_info rp;
Johan Hedberg03811012010-12-08 00:21:06 +0200630
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200631 BT_DBG("sock %p %s", sk, hdev->name);
Johan Hedberg03811012010-12-08 00:21:06 +0200632
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300633 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200634
Johan Hedberg7d785252011-12-15 00:47:39 +0200635 if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
636 mgmt_init_hdev(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200637
638 memset(&rp, 0, sizeof(rp));
639
Johan Hedberg03811012010-12-08 00:21:06 +0200640 bacpy(&rp.bdaddr, &hdev->bdaddr);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200641
642 rp.version = hdev->hci_ver;
643
Johan Hedberg03811012010-12-08 00:21:06 +0200644 put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200645
646 rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
647 rp.current_settings = cpu_to_le32(get_current_settings(hdev));
648
649 memcpy(rp.dev_class, hdev->dev_class, 3);
Johan Hedberg03811012010-12-08 00:21:06 +0200650
651 memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
Johan Hedberg27fcc362012-02-22 21:46:22 +0200652 memcpy(rp.short_name, hdev->short_name, sizeof(hdev->short_name));
Johan Hedberg03811012010-12-08 00:21:06 +0200653
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300654 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200655
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200656 return cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp,
657 sizeof(rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200658}
659
660static void mgmt_pending_free(struct pending_cmd *cmd)
661{
662 sock_put(cmd->sk);
663 kfree(cmd->param);
664 kfree(cmd);
665}
666
667static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
668 struct hci_dev *hdev,
669 void *data, u16 len)
670{
671 struct pending_cmd *cmd;
672
673 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
674 if (!cmd)
675 return NULL;
676
677 cmd->opcode = opcode;
678 cmd->index = hdev->id;
679
680 cmd->param = kmalloc(len, GFP_ATOMIC);
681 if (!cmd->param) {
682 kfree(cmd);
683 return NULL;
684 }
685
686 if (data)
687 memcpy(cmd->param, data, len);
688
689 cmd->sk = sk;
690 sock_hold(sk);
691
692 list_add(&cmd->list, &hdev->mgmt_pending);
693
694 return cmd;
695}
696
697static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
698 void (*cb)(struct pending_cmd *cmd, void *data),
699 void *data)
700{
701 struct list_head *p, *n;
702
703 list_for_each_safe(p, n, &hdev->mgmt_pending) {
704 struct pending_cmd *cmd;
705
706 cmd = list_entry(p, struct pending_cmd, list);
707
708 if (opcode > 0 && cmd->opcode != opcode)
709 continue;
710
711 cb(cmd, data);
712 }
713}
714
715static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
716{
717 struct pending_cmd *cmd;
718
719 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
720 if (cmd->opcode == opcode)
721 return cmd;
722 }
723
724 return NULL;
725}
726
727static void mgmt_pending_remove(struct pending_cmd *cmd)
728{
729 list_del(&cmd->list);
730 mgmt_pending_free(cmd);
731}
732
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200733static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
Johan Hedberg86805702011-11-11 16:18:52 +0200734{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200735 __le32 settings = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg86805702011-11-11 16:18:52 +0200736
Johan Hedbergaee9b212012-02-18 15:07:59 +0200737 return cmd_complete(sk, hdev->id, opcode, 0, &settings,
738 sizeof(settings));
Johan Hedberg86805702011-11-11 16:18:52 +0200739}
740
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200741static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data,
742 u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200743{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300744 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200745 struct pending_cmd *cmd;
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200746 int err;
Johan Hedberg03811012010-12-08 00:21:06 +0200747
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200748 BT_DBG("request for %s", hdev->name);
Johan Hedberg03811012010-12-08 00:21:06 +0200749
750 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200751 return cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
Johan Hedbergca69b792011-11-11 18:10:00 +0200752 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200753
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300754 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200755
Marcel Holtmannf0d4b782012-02-21 12:14:25 +0100756 if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) {
757 cancel_delayed_work(&hdev->power_off);
758
759 if (cp->val) {
760 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
761 mgmt_powered(hdev, 1);
762 goto failed;
763 }
764 }
765
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200766 if (!!cp->val == hdev_is_powered(hdev)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200767 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200768 goto failed;
769 }
770
771 if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200772 err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED,
Johan Hedbergca69b792011-11-11 18:10:00 +0200773 MGMT_STATUS_BUSY);
Johan Hedberg03811012010-12-08 00:21:06 +0200774 goto failed;
775 }
776
777 cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
778 if (!cmd) {
779 err = -ENOMEM;
780 goto failed;
781 }
782
783 if (cp->val)
Gustavo F. Padovan7f971042011-12-18 12:40:32 -0200784 schedule_work(&hdev->power_on);
Johan Hedberg03811012010-12-08 00:21:06 +0200785 else
Gustavo F. Padovan80b7ab32011-12-17 14:52:27 -0200786 schedule_work(&hdev->power_off.work);
Johan Hedberg03811012010-12-08 00:21:06 +0200787
788 err = 0;
789
790failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300791 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200792 return err;
793}
794
Johan Hedbergbeadb2b2012-02-21 16:55:31 +0200795static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
796 u16 data_len, struct sock *skip_sk)
797{
798 struct sk_buff *skb;
799 struct mgmt_hdr *hdr;
800
801 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
802 if (!skb)
803 return -ENOMEM;
804
805 hdr = (void *) skb_put(skb, sizeof(*hdr));
806 hdr->opcode = cpu_to_le16(event);
807 if (hdev)
808 hdr->index = cpu_to_le16(hdev->id);
809 else
810 hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
811 hdr->len = cpu_to_le16(data_len);
812
813 if (data)
814 memcpy(skb_put(skb, data_len), data, data_len);
815
Marcel Holtmann97e0bde2012-02-22 13:49:28 +0100816 /* Time stamp */
817 __net_timestamp(skb);
818
Johan Hedbergbeadb2b2012-02-21 16:55:31 +0200819 hci_send_to_control(skb, skip_sk);
820 kfree_skb(skb);
821
822 return 0;
823}
824
825static int new_settings(struct hci_dev *hdev, struct sock *skip)
826{
827 __le32 ev;
828
829 ev = cpu_to_le32(get_current_settings(hdev));
830
831 return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip);
832}
833
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200834static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
835 u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200836{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300837 struct mgmt_cp_set_discoverable *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200838 struct pending_cmd *cmd;
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200839 u16 timeout;
Johan Hedberg03811012010-12-08 00:21:06 +0200840 u8 scan;
841 int err;
842
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200843 BT_DBG("request for %s", hdev->name);
Johan Hedberg03811012010-12-08 00:21:06 +0200844
845 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200846 return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +0200847 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200848
Marcel Holtmann24c54a92012-02-22 18:06:34 +0100849 timeout = get_unaligned_le16(&cp->timeout);
850 if (!cp->val && timeout > 0)
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200851 return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +0200852 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200853
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300854 hci_dev_lock(hdev);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200855
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200856 if (!hdev_is_powered(hdev) && timeout > 0) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200857 err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +0200858 MGMT_STATUS_NOT_POWERED);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200859 goto failed;
860 }
861
862 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
863 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200864 err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +0200865 MGMT_STATUS_BUSY);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200866 goto failed;
867 }
868
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200869 if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200870 err = cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE,
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200871 MGMT_STATUS_REJECTED);
872 goto failed;
873 }
874
875 if (!hdev_is_powered(hdev)) {
Johan Hedberg0224d2f2012-02-21 19:40:05 +0200876 bool changed = false;
877
878 if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
879 change_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
880 changed = true;
881 }
882
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200883 err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
Johan Hedberg0224d2f2012-02-21 19:40:05 +0200884 if (err < 0)
885 goto failed;
886
887 if (changed)
888 err = new_settings(hdev, sk);
889
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200890 goto failed;
891 }
892
893 if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) {
Marcel Holtmann955638e2012-02-22 18:21:00 +0100894 if (hdev->discov_timeout > 0) {
895 cancel_delayed_work(&hdev->discov_off);
896 hdev->discov_timeout = 0;
897 }
898
899 if (cp->val && timeout > 0) {
900 hdev->discov_timeout = timeout;
901 queue_delayed_work(hdev->workqueue, &hdev->discov_off,
902 msecs_to_jiffies(hdev->discov_timeout * 1000));
903 }
904
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200905 err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200906 goto failed;
907 }
908
909 cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
910 if (!cmd) {
911 err = -ENOMEM;
912 goto failed;
913 }
914
915 scan = SCAN_PAGE;
916
917 if (cp->val)
918 scan |= SCAN_INQUIRY;
919 else
920 cancel_delayed_work(&hdev->discov_off);
921
922 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
923 if (err < 0)
924 mgmt_pending_remove(cmd);
925
Johan Hedberg03811012010-12-08 00:21:06 +0200926 if (cp->val)
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200927 hdev->discov_timeout = timeout;
Johan Hedberg03811012010-12-08 00:21:06 +0200928
929failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300930 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200931 return err;
932}
933
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200934static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data,
935 u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200936{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300937 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200938 struct pending_cmd *cmd;
939 u8 scan;
940 int err;
941
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200942 BT_DBG("request for %s", hdev->name);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200943
Johan Hedberg03811012010-12-08 00:21:06 +0200944 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200945 return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +0200946 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200947
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300948 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200949
Johan Hedberg4b34ee782012-02-21 14:13:02 +0200950 if (!hdev_is_powered(hdev)) {
Johan Hedberg0224d2f2012-02-21 19:40:05 +0200951 bool changed = false;
952
953 if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
954 changed = true;
955
Andrei Emeltchenko6bf0e462012-02-22 13:21:16 +0200956 if (cp->val) {
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200957 set_bit(HCI_CONNECTABLE, &hdev->dev_flags);
Andrei Emeltchenko6bf0e462012-02-22 13:21:16 +0200958 } else {
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200959 clear_bit(HCI_CONNECTABLE, &hdev->dev_flags);
960 clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags);
961 }
Johan Hedberg0224d2f2012-02-21 19:40:05 +0200962
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200963 err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
Johan Hedberg0224d2f2012-02-21 19:40:05 +0200964 if (err < 0)
965 goto failed;
966
967 if (changed)
968 err = new_settings(hdev, sk);
969
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200970 goto failed;
971 }
972
973 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
974 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +0200975 err = cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +0200976 MGMT_STATUS_BUSY);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200977 goto failed;
978 }
979
Johan Hedberg5e5282b2012-02-21 16:01:30 +0200980 if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200981 err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200982 goto failed;
983 }
984
985 cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
986 if (!cmd) {
987 err = -ENOMEM;
988 goto failed;
989 }
990
Andrei Emeltchenko6bf0e462012-02-22 13:21:16 +0200991 if (cp->val) {
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200992 scan = SCAN_PAGE;
Andrei Emeltchenko6bf0e462012-02-22 13:21:16 +0200993 } else {
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200994 scan = 0;
995
Johan Hedbergdf2c6c52012-02-21 19:15:49 +0200996 if (test_bit(HCI_ISCAN, &hdev->flags) &&
997 hdev->discov_timeout > 0)
998 cancel_delayed_work(&hdev->discov_off);
999 }
1000
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001001 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
1002 if (err < 0)
1003 mgmt_pending_remove(cmd);
1004
1005failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001006 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001007 return err;
1008}
1009
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001010static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data,
1011 u16 len)
Johan Hedberg73f22f62010-12-29 16:00:25 +02001012{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001013 struct mgmt_mode *cp = data;
Johan Hedberg73f22f62010-12-29 16:00:25 +02001014 int err;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001015
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001016 BT_DBG("request for %s", hdev->name);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001017
1018 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001019 return cmd_status(sk, hdev->id, MGMT_OP_SET_PAIRABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001020 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001021
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001022 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001023
1024 if (cp->val)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001025 set_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001026 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001027 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001028
Johan Hedberg69ab39e2011-12-15 00:47:35 +02001029 err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001030 if (err < 0)
1031 goto failed;
1032
Johan Hedbergbeadb2b2012-02-21 16:55:31 +02001033 err = new_settings(hdev, sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001034
1035failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001036 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02001037 return err;
1038}
Johan Hedberg72a734e2010-12-30 00:38:22 +02001039
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001040static int set_link_security(struct sock *sk, struct hci_dev *hdev,
1041 void *data, u16 len)
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001042{
1043 struct mgmt_mode *cp = data;
1044 struct pending_cmd *cmd;
Johan Hedberg816a11d2012-02-26 13:04:52 +02001045 u8 val;
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001046 int err;
1047
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001048 BT_DBG("request for %s", hdev->name);
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001049
1050 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001051 return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001052 MGMT_STATUS_INVALID_PARAMS);
1053
1054 hci_dev_lock(hdev);
1055
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001056 if (!hdev_is_powered(hdev)) {
Johan Hedberg47990ea2012-02-22 11:58:37 +02001057 bool changed = false;
1058
1059 if (!!cp->val != test_bit(HCI_LINK_SECURITY,
1060 &hdev->dev_flags)) {
1061 change_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
1062 changed = true;
1063 }
1064
1065 err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
1066 if (err < 0)
1067 goto failed;
1068
1069 if (changed)
1070 err = new_settings(hdev, sk);
1071
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001072 goto failed;
1073 }
1074
1075 if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001076 err = cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY,
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001077 MGMT_STATUS_BUSY);
1078 goto failed;
1079 }
1080
1081 val = !!cp->val;
1082
1083 if (test_bit(HCI_AUTH, &hdev->flags) == val) {
1084 err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
1085 goto failed;
1086 }
1087
1088 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len);
1089 if (!cmd) {
1090 err = -ENOMEM;
1091 goto failed;
1092 }
1093
1094 err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val);
1095 if (err < 0) {
1096 mgmt_pending_remove(cmd);
1097 goto failed;
1098 }
1099
1100failed:
1101 hci_dev_unlock(hdev);
Johan Hedberg33ef95e2012-02-16 23:56:27 +02001102 return err;
1103}
1104
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001105static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001106{
1107 struct mgmt_mode *cp = data;
1108 struct pending_cmd *cmd;
Johan Hedberg816a11d2012-02-26 13:04:52 +02001109 u8 val;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001110 int err;
1111
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001112 BT_DBG("request for %s", hdev->name);
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001113
1114 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001115 return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001116 MGMT_STATUS_INVALID_PARAMS);
1117
1118 hci_dev_lock(hdev);
1119
Johan Hedberg6c8f12c2012-02-22 16:35:26 +02001120 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001121 err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
Johan Hedberg6c8f12c2012-02-22 16:35:26 +02001122 MGMT_STATUS_NOT_SUPPORTED);
1123 goto failed;
1124 }
1125
Johan Hedbergc0ecddc2012-02-22 12:38:31 +02001126 val = !!cp->val;
1127
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001128 if (!hdev_is_powered(hdev)) {
Johan Hedbergc0ecddc2012-02-22 12:38:31 +02001129 bool changed = false;
1130
1131 if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
1132 change_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
1133 changed = true;
1134 }
1135
1136 err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
1137 if (err < 0)
1138 goto failed;
1139
1140 if (changed)
1141 err = new_settings(hdev, sk);
1142
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001143 goto failed;
1144 }
1145
1146 if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001147 err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP,
1148 MGMT_STATUS_BUSY);
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001149 goto failed;
1150 }
1151
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001152 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
1153 err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
1154 goto failed;
1155 }
1156
1157 cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len);
1158 if (!cmd) {
1159 err = -ENOMEM;
1160 goto failed;
1161 }
1162
1163 err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
1164 if (err < 0) {
1165 mgmt_pending_remove(cmd);
1166 goto failed;
1167 }
1168
1169failed:
1170 hci_dev_unlock(hdev);
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001171 return err;
1172}
1173
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001174static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001175{
1176 struct mgmt_mode *cp = data;
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001177
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001178 BT_DBG("request for %s", hdev->name);
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001179
1180 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001181 return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001182 MGMT_STATUS_INVALID_PARAMS);
1183
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001184 if (!enable_hs)
1185 return cmd_status(sk, hdev->id, MGMT_OP_SET_HS,
1186 MGMT_STATUS_NOT_SUPPORTED);
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001187
1188 if (cp->val)
1189 set_bit(HCI_HS_ENABLED, &hdev->dev_flags);
1190 else
1191 clear_bit(HCI_HS_ENABLED, &hdev->dev_flags);
1192
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001193 return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev);
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02001194}
1195
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001196static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
Johan Hedberg06199cf2012-02-22 16:37:11 +02001197{
1198 struct mgmt_mode *cp = data;
1199 struct hci_cp_write_le_host_supported hci_cp;
1200 struct pending_cmd *cmd;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001201 int err;
Johan Hedberg0b60eba2012-02-28 00:57:24 +02001202 u8 val, enabled;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001203
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001204 BT_DBG("request for %s", hdev->name);
Johan Hedberg06199cf2012-02-22 16:37:11 +02001205
1206 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001207 return cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
Johan Hedberg06199cf2012-02-22 16:37:11 +02001208 MGMT_STATUS_INVALID_PARAMS);
1209
Johan Hedberg1de028c2012-02-29 19:55:35 -08001210 hci_dev_lock(hdev);
1211
Johan Hedberg06199cf2012-02-22 16:37:11 +02001212 if (!enable_le || !(hdev->features[4] & LMP_LE)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001213 err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
Johan Hedberg06199cf2012-02-22 16:37:11 +02001214 MGMT_STATUS_NOT_SUPPORTED);
Johan Hedberg1de028c2012-02-29 19:55:35 -08001215 goto unlock;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001216 }
1217
1218 val = !!cp->val;
Johan Hedberg0b60eba2012-02-28 00:57:24 +02001219 enabled = !!(hdev->host_features[0] & LMP_HOST_LE);
Johan Hedberg06199cf2012-02-22 16:37:11 +02001220
Johan Hedberg0b60eba2012-02-28 00:57:24 +02001221 if (!hdev_is_powered(hdev) || val == enabled) {
Johan Hedberg06199cf2012-02-22 16:37:11 +02001222 bool changed = false;
1223
1224 if (val != test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
1225 change_bit(HCI_LE_ENABLED, &hdev->dev_flags);
1226 changed = true;
1227 }
1228
1229 err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev);
1230 if (err < 0)
Johan Hedberg1de028c2012-02-29 19:55:35 -08001231 goto unlock;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001232
1233 if (changed)
1234 err = new_settings(hdev, sk);
1235
Johan Hedberg1de028c2012-02-29 19:55:35 -08001236 goto unlock;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001237 }
1238
1239 if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001240 err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE,
1241 MGMT_STATUS_BUSY);
Johan Hedberg1de028c2012-02-29 19:55:35 -08001242 goto unlock;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001243 }
1244
1245 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len);
1246 if (!cmd) {
1247 err = -ENOMEM;
Johan Hedberg1de028c2012-02-29 19:55:35 -08001248 goto unlock;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001249 }
1250
1251 memset(&hci_cp, 0, sizeof(hci_cp));
1252
1253 if (val) {
1254 hci_cp.le = val;
1255 hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
1256 }
1257
1258 err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
1259 sizeof(hci_cp), &hci_cp);
1260 if (err < 0) {
1261 mgmt_pending_remove(cmd);
Johan Hedberg1de028c2012-02-29 19:55:35 -08001262 goto unlock;
Johan Hedberg06199cf2012-02-22 16:37:11 +02001263 }
1264
Johan Hedberg1de028c2012-02-29 19:55:35 -08001265unlock:
1266 hci_dev_unlock(hdev);
Johan Hedberg06199cf2012-02-22 16:37:11 +02001267 return err;
1268}
1269
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001270static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001271{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001272 struct mgmt_cp_add_uuid *cp = data;
Johan Hedberg90e70452012-02-23 23:09:40 +02001273 struct pending_cmd *cmd;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001274 struct bt_uuid *uuid;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001275 int err;
1276
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001277 BT_DBG("request for %s", hdev->name);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001278
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001279 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001280 return cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID,
Johan Hedbergca69b792011-11-11 18:10:00 +02001281 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001282
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001283 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001284
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02001285 if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001286 err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID,
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02001287 MGMT_STATUS_BUSY);
1288 goto failed;
1289 }
1290
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001291 uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
1292 if (!uuid) {
1293 err = -ENOMEM;
1294 goto failed;
1295 }
1296
1297 memcpy(uuid->uuid, cp->uuid, 16);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001298 uuid->svc_hint = cp->svc_hint;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001299
1300 list_add(&uuid->list, &hdev->uuids);
1301
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001302 err = update_class(hdev);
1303 if (err < 0)
1304 goto failed;
1305
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001306 err = update_eir(hdev);
1307 if (err < 0)
1308 goto failed;
1309
Johan Hedberg90e70452012-02-23 23:09:40 +02001310 if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001311 err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0,
Johan Hedberg90e70452012-02-23 23:09:40 +02001312 hdev->dev_class, 3);
1313 goto failed;
1314 }
1315
1316 cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len);
1317 if (!cmd) {
1318 err = -ENOMEM;
1319 goto failed;
1320 }
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001321
1322failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001323 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001324 return err;
1325}
1326
Johan Hedberg24b78d02012-02-23 23:24:30 +02001327static bool enable_service_cache(struct hci_dev *hdev)
1328{
1329 if (!hdev_is_powered(hdev))
1330 return false;
1331
1332 if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
1333 schedule_delayed_work(&hdev->service_cache,
1334 msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
1335 return true;
1336 }
1337
1338 return false;
1339}
1340
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001341static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data,
1342 u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001343{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001344 struct mgmt_cp_remove_uuid *cp = data;
Johan Hedberg90e70452012-02-23 23:09:40 +02001345 struct pending_cmd *cmd;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001346 struct list_head *p, *n;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001347 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 +02001348 int err, found;
1349
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001350 BT_DBG("request for %s", hdev->name);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001351
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001352 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001353 return cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
Johan Hedbergca69b792011-11-11 18:10:00 +02001354 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001355
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001356 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001357
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02001358 if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001359 err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02001360 MGMT_STATUS_BUSY);
1361 goto unlock;
1362 }
1363
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001364 if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
1365 err = hci_uuids_clear(hdev);
Johan Hedberg4004b6d2012-02-23 21:30:12 +02001366
Johan Hedberg24b78d02012-02-23 23:24:30 +02001367 if (enable_service_cache(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001368 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID,
1369 0, hdev->dev_class, 3);
Johan Hedberg24b78d02012-02-23 23:24:30 +02001370 goto unlock;
1371 }
Johan Hedberg4004b6d2012-02-23 21:30:12 +02001372
Johan Hedberg9246a862012-02-23 21:33:16 +02001373 goto update_class;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001374 }
1375
1376 found = 0;
1377
1378 list_for_each_safe(p, n, &hdev->uuids) {
1379 struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
1380
1381 if (memcmp(match->uuid, cp->uuid, 16) != 0)
1382 continue;
1383
1384 list_del(&match->list);
1385 found++;
1386 }
1387
1388 if (found == 0) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001389 err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID,
Johan Hedbergca69b792011-11-11 18:10:00 +02001390 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001391 goto unlock;
1392 }
1393
Johan Hedberg9246a862012-02-23 21:33:16 +02001394update_class:
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001395 err = update_class(hdev);
1396 if (err < 0)
1397 goto unlock;
1398
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001399 err = update_eir(hdev);
1400 if (err < 0)
1401 goto unlock;
1402
Johan Hedberg90e70452012-02-23 23:09:40 +02001403 if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001404 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0,
Johan Hedberg9997a532012-02-23 15:57:46 +02001405 hdev->dev_class, 3);
Johan Hedberg90e70452012-02-23 23:09:40 +02001406 goto unlock;
1407 }
1408
1409 cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len);
1410 if (!cmd) {
1411 err = -ENOMEM;
1412 goto unlock;
1413 }
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001414
1415unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001416 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001417 return err;
1418}
1419
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001420static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data,
1421 u16 len)
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001422{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001423 struct mgmt_cp_set_dev_class *cp = data;
Johan Hedberg90e70452012-02-23 23:09:40 +02001424 struct pending_cmd *cmd;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001425 int err;
1426
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001427 BT_DBG("request for %s", hdev->name);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001428
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001429 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001430 return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
Johan Hedbergca69b792011-11-11 18:10:00 +02001431 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001432
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001433 hci_dev_lock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001434
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02001435 if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001436 err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS,
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02001437 MGMT_STATUS_BUSY);
1438 goto unlock;
1439 }
1440
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001441 hdev->major_class = cp->major;
1442 hdev->minor_class = cp->minor;
1443
Johan Hedberg932f5ff2012-02-22 22:11:32 +02001444 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001445 err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
Johan Hedberg932f5ff2012-02-22 22:11:32 +02001446 hdev->dev_class, 3);
1447 goto unlock;
1448 }
1449
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001450 if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
Johan Hedberg7d785252011-12-15 00:47:39 +02001451 hci_dev_unlock(hdev);
1452 cancel_delayed_work_sync(&hdev->service_cache);
1453 hci_dev_lock(hdev);
Johan Hedberg14c0b602011-12-15 00:47:37 +02001454 update_eir(hdev);
Johan Hedberg7d785252011-12-15 00:47:39 +02001455 }
Johan Hedberg14c0b602011-12-15 00:47:37 +02001456
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001457 err = update_class(hdev);
Johan Hedberg90e70452012-02-23 23:09:40 +02001458 if (err < 0)
1459 goto unlock;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001460
Johan Hedberg90e70452012-02-23 23:09:40 +02001461 if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001462 err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0,
Johan Hedberg8ec37032012-02-22 22:02:50 +02001463 hdev->dev_class, 3);
Johan Hedberg90e70452012-02-23 23:09:40 +02001464 goto unlock;
1465 }
1466
1467 cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len);
1468 if (!cmd) {
1469 err = -ENOMEM;
1470 goto unlock;
1471 }
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001472
Johan Hedbergb5235a62012-02-21 14:32:24 +02001473unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001474 hci_dev_unlock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001475 return err;
1476}
1477
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001478static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data,
1479 u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001480{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001481 struct mgmt_cp_load_link_keys *cp = data;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001482 u16 key_count, expected_len;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001483 int i;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001484
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001485 if (len < sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001486 return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
Johan Hedbergca69b792011-11-11 18:10:00 +02001487 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001488
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001489 key_count = get_unaligned_le16(&cp->key_count);
1490
Johan Hedberg86742e12011-11-07 23:13:38 +02001491 expected_len = sizeof(*cp) + key_count *
1492 sizeof(struct mgmt_link_key_info);
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001493 if (expected_len != len) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001494 BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001495 len, expected_len);
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001496 return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS,
Johan Hedbergca69b792011-11-11 18:10:00 +02001497 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001498 }
1499
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001500 BT_DBG("%s debug_keys %u key_count %u", hdev->name, cp->debug_keys,
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001501 key_count);
1502
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001503 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001504
1505 hci_link_keys_clear(hdev);
1506
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001507 set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001508
1509 if (cp->debug_keys)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001510 set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001511 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001512 clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001513
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001514 for (i = 0; i < key_count; i++) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001515 struct mgmt_link_key_info *key = &cp->keys[i];
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001516
Johan Hedbergd753fdc2012-02-17 14:06:34 +02001517 hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
1518 key->type, key->pin_len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001519 }
1520
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001521 cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0);
Johan Hedberg0e5f8752011-11-11 16:18:54 +02001522
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001523 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001524
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001525 return 0;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001526}
1527
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001528static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
1529 u8 addr_type, struct sock *skip_sk)
1530{
1531 struct mgmt_ev_device_unpaired ev;
1532
1533 bacpy(&ev.addr.bdaddr, bdaddr);
1534 ev.addr.type = addr_type;
1535
1536 return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev),
1537 skip_sk);
1538}
1539
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001540static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
1541 u16 len)
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001542{
Johan Hedberg124f6e32012-02-09 13:50:12 +02001543 struct mgmt_cp_unpair_device *cp = data;
1544 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001545 struct hci_cp_disconnect dc;
1546 struct pending_cmd *cmd;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001547 struct hci_conn *conn;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001548 int err;
1549
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001550 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001551 return cmd_status(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001552 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001553
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001554 hci_dev_lock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001555
Johan Hedberga8a1d192011-11-10 15:54:38 +02001556 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02001557 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1558 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001559
Johan Hedberg86a8cfc2012-02-22 22:53:34 +02001560 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001561 err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
Johan Hedberg86a8cfc2012-02-22 22:53:34 +02001562 MGMT_STATUS_NOT_POWERED,
1563 &rp, sizeof(rp));
1564 goto unlock;
1565 }
1566
Johan Hedberg124f6e32012-02-09 13:50:12 +02001567 if (cp->addr.type == MGMT_ADDR_BREDR)
1568 err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
1569 else
1570 err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
Vinicius Costa Gomesb0dbfb42012-02-02 21:08:03 -03001571
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001572 if (err < 0) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001573 err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE,
Johan Hedberg86a8cfc2012-02-22 22:53:34 +02001574 MGMT_STATUS_NOT_PAIRED,
1575 &rp, sizeof(rp));
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001576 goto unlock;
1577 }
1578
Johan Hedberg86a8cfc2012-02-22 22:53:34 +02001579 if (cp->disconnect) {
1580 if (cp->addr.type == MGMT_ADDR_BREDR)
1581 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
1582 &cp->addr.bdaddr);
1583 else
1584 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
1585 &cp->addr.bdaddr);
1586 } else {
1587 conn = NULL;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001588 }
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001589
Johan Hedberga8a1d192011-11-10 15:54:38 +02001590 if (!conn) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001591 err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
Johan Hedbergaee9b212012-02-18 15:07:59 +02001592 &rp, sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001593 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberga8a1d192011-11-10 15:54:38 +02001594 goto unlock;
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001595 }
1596
Johan Hedberg124f6e32012-02-09 13:50:12 +02001597 cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
1598 sizeof(*cp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02001599 if (!cmd) {
1600 err = -ENOMEM;
1601 goto unlock;
1602 }
1603
1604 put_unaligned_le16(conn->handle, &dc.handle);
1605 dc.reason = 0x13; /* Remote User Terminated Connection */
1606 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1607 if (err < 0)
1608 mgmt_pending_remove(cmd);
1609
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001610unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001611 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02001612 return err;
1613}
1614
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001615static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
1616 u16 len)
Johan Hedberg8962ee72011-01-20 12:40:27 +02001617{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001618 struct mgmt_cp_disconnect *cp = data;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001619 struct hci_cp_disconnect dc;
Johan Hedberg366a0332011-02-19 12:05:55 -03001620 struct pending_cmd *cmd;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001621 struct hci_conn *conn;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001622 int err;
1623
1624 BT_DBG("");
1625
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001626 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001627 return cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
Johan Hedbergca69b792011-11-11 18:10:00 +02001628 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001629
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001630 hci_dev_lock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001631
1632 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001633 err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
Johan Hedbergca69b792011-11-11 18:10:00 +02001634 MGMT_STATUS_NOT_POWERED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001635 goto failed;
1636 }
1637
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001638 if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001639 err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
Johan Hedbergca69b792011-11-11 18:10:00 +02001640 MGMT_STATUS_BUSY);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001641 goto failed;
1642 }
1643
Johan Hedberg88c3df12012-02-09 14:27:38 +02001644 if (cp->addr.type == MGMT_ADDR_BREDR)
1645 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
1646 else
1647 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
Vinicius Costa Gomes365227e2011-05-06 18:41:44 -03001648
Johan Hedberg8962ee72011-01-20 12:40:27 +02001649 if (!conn) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001650 err = cmd_status(sk, hdev->id, MGMT_OP_DISCONNECT,
Johan Hedbergca69b792011-11-11 18:10:00 +02001651 MGMT_STATUS_NOT_CONNECTED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001652 goto failed;
1653 }
1654
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001655 cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001656 if (!cmd) {
1657 err = -ENOMEM;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001658 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001659 }
Johan Hedberg8962ee72011-01-20 12:40:27 +02001660
1661 put_unaligned_le16(conn->handle, &dc.handle);
1662 dc.reason = 0x13; /* Remote User Terminated Connection */
1663
1664 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1665 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001666 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001667
1668failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001669 hci_dev_unlock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001670 return err;
1671}
1672
Johan Hedberg48264f02011-11-09 13:58:58 +02001673static u8 link_to_mgmt(u8 link_type, u8 addr_type)
Johan Hedberg4c659c32011-11-07 23:13:39 +02001674{
1675 switch (link_type) {
1676 case LE_LINK:
Johan Hedberg48264f02011-11-09 13:58:58 +02001677 switch (addr_type) {
1678 case ADDR_LE_DEV_PUBLIC:
1679 return MGMT_ADDR_LE_PUBLIC;
1680 case ADDR_LE_DEV_RANDOM:
1681 return MGMT_ADDR_LE_RANDOM;
1682 default:
1683 return MGMT_ADDR_INVALID;
1684 }
Johan Hedberg4c659c32011-11-07 23:13:39 +02001685 case ACL_LINK:
1686 return MGMT_ADDR_BREDR;
1687 default:
1688 return MGMT_ADDR_INVALID;
1689 }
1690}
1691
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001692static int get_connections(struct sock *sk, struct hci_dev *hdev)
Johan Hedberg2784eb42011-01-21 13:56:35 +02001693{
Johan Hedberg2784eb42011-01-21 13:56:35 +02001694 struct mgmt_rp_get_connections *rp;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001695 struct hci_conn *c;
Johan Hedberga38528f2011-01-22 06:46:43 +02001696 size_t rp_len;
Johan Hedberg60fc5fb62012-02-23 09:52:28 +02001697 int err;
1698 u16 i;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001699
1700 BT_DBG("");
1701
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001702 hci_dev_lock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001703
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02001704 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001705 err = cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS,
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02001706 MGMT_STATUS_NOT_POWERED);
1707 goto unlock;
1708 }
1709
Johan Hedberg60fc5fb62012-02-23 09:52:28 +02001710 i = 0;
Johan Hedbergb644ba32012-01-17 21:48:47 +02001711 list_for_each_entry(c, &hdev->conn_hash.list, list) {
1712 if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
Johan Hedberg60fc5fb62012-02-23 09:52:28 +02001713 i++;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001714 }
1715
Johan Hedberg60fc5fb62012-02-23 09:52:28 +02001716 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberga38528f2011-01-22 06:46:43 +02001717 rp = kmalloc(rp_len, GFP_ATOMIC);
1718 if (!rp) {
Johan Hedberg2784eb42011-01-21 13:56:35 +02001719 err = -ENOMEM;
1720 goto unlock;
1721 }
1722
Johan Hedberg2784eb42011-01-21 13:56:35 +02001723 i = 0;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001724 list_for_each_entry(c, &hdev->conn_hash.list, list) {
Johan Hedbergb644ba32012-01-17 21:48:47 +02001725 if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1726 continue;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001727 bacpy(&rp->addr[i].bdaddr, &c->dst);
Johan Hedberg48264f02011-11-09 13:58:58 +02001728 rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
Johan Hedberg4c659c32011-11-07 23:13:39 +02001729 if (rp->addr[i].type == MGMT_ADDR_INVALID)
1730 continue;
1731 i++;
1732 }
1733
Johan Hedberg60fc5fb62012-02-23 09:52:28 +02001734 put_unaligned_le16(i, &rp->conn_count);
1735
Johan Hedberg4c659c32011-11-07 23:13:39 +02001736 /* Recalculate length in case of filtered SCO connections, etc */
1737 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberg2784eb42011-01-21 13:56:35 +02001738
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001739 err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp,
1740 rp_len);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001741
Johan Hedberga38528f2011-01-22 06:46:43 +02001742 kfree(rp);
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02001743
1744unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001745 hci_dev_unlock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001746 return err;
1747}
1748
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001749static int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
1750 struct mgmt_cp_pin_code_neg_reply *cp)
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001751{
1752 struct pending_cmd *cmd;
1753 int err;
1754
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001755 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001756 sizeof(*cp));
1757 if (!cmd)
1758 return -ENOMEM;
1759
Johan Hedbergd8457692012-02-17 14:24:57 +02001760 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
1761 sizeof(cp->addr.bdaddr), &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001762 if (err < 0)
1763 mgmt_pending_remove(cmd);
1764
1765 return err;
1766}
1767
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001768static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data,
1769 u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001770{
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001771 struct hci_conn *conn;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001772 struct mgmt_cp_pin_code_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001773 struct hci_cp_pin_code_reply reply;
Johan Hedberg366a0332011-02-19 12:05:55 -03001774 struct pending_cmd *cmd;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001775 int err;
1776
1777 BT_DBG("");
1778
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001779 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001780 return cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001781 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001782
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001783 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001784
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001785 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001786 err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001787 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001788 goto failed;
1789 }
1790
Johan Hedbergd8457692012-02-17 14:24:57 +02001791 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001792 if (!conn) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001793 err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001794 MGMT_STATUS_NOT_CONNECTED);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001795 goto failed;
1796 }
1797
1798 if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
Johan Hedbergd8457692012-02-17 14:24:57 +02001799 struct mgmt_cp_pin_code_neg_reply ncp;
1800
1801 memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr));
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001802
1803 BT_ERR("PIN code is not 16 bytes long");
1804
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001805 err = send_pin_code_neg_reply(sk, hdev, &ncp);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001806 if (err >= 0)
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001807 err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001808 MGMT_STATUS_INVALID_PARAMS);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001809
1810 goto failed;
1811 }
1812
Gustavo F. Padovan00abfe42012-03-01 00:37:10 -03001813 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001814 if (!cmd) {
1815 err = -ENOMEM;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001816 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001817 }
Johan Hedberg980e1a52011-01-22 06:10:07 +02001818
Johan Hedbergd8457692012-02-17 14:24:57 +02001819 bacpy(&reply.bdaddr, &cp->addr.bdaddr);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001820 reply.pin_len = cp->pin_len;
Waldemar Rymarkiewicz24718ca2011-06-01 17:28:47 +02001821 memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
Johan Hedberg980e1a52011-01-22 06:10:07 +02001822
1823 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
1824 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001825 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001826
1827failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001828 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001829 return err;
1830}
1831
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001832static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev,
1833 void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001834{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001835 struct mgmt_cp_pin_code_neg_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001836 int err;
1837
1838 BT_DBG("");
1839
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001840 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001841 return cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001842 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001843
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001844 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001845
Johan Hedberg4b34ee782012-02-21 14:13:02 +02001846 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001847 err = cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001848 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001849 goto failed;
1850 }
1851
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001852 err = send_pin_code_neg_reply(sk, hdev, cp);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001853
1854failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001855 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001856 return err;
1857}
1858
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001859static int set_io_capability(struct sock *sk, struct hci_dev *hdev,
1860 void *data, u16 len)
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001861{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001862 struct mgmt_cp_set_io_capability *cp = data;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001863
1864 BT_DBG("");
1865
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001866 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001867 return cmd_status(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001868 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001869
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001870 hci_dev_lock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001871
1872 hdev->io_capability = cp->io_capability;
1873
1874 BT_DBG("%s IO capability set to 0x%02x", hdev->name,
Szymon Jancb8534e02011-03-01 16:55:34 +01001875 hdev->io_capability);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001876
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001877 hci_dev_unlock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001878
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001879 return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0,
1880 NULL, 0);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001881}
1882
Johan Hedberge9a416b2011-02-19 12:05:56 -03001883static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1884{
1885 struct hci_dev *hdev = conn->hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001886 struct pending_cmd *cmd;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001887
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001888 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
Johan Hedberge9a416b2011-02-19 12:05:56 -03001889 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1890 continue;
1891
Johan Hedberge9a416b2011-02-19 12:05:56 -03001892 if (cmd->user_data != conn)
1893 continue;
1894
1895 return cmd;
1896 }
1897
1898 return NULL;
1899}
1900
1901static void pairing_complete(struct pending_cmd *cmd, u8 status)
1902{
1903 struct mgmt_rp_pair_device rp;
1904 struct hci_conn *conn = cmd->user_data;
1905
Johan Hedbergba4e5642011-11-11 00:07:34 +02001906 bacpy(&rp.addr.bdaddr, &conn->dst);
1907 rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001908
Johan Hedbergaee9b212012-02-18 15:07:59 +02001909 cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
1910 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001911
1912 /* So we don't get further callbacks for this connection */
1913 conn->connect_cfm_cb = NULL;
1914 conn->security_cfm_cb = NULL;
1915 conn->disconn_cfm_cb = NULL;
1916
1917 hci_conn_put(conn);
1918
Johan Hedberga664b5b2011-02-19 12:06:02 -03001919 mgmt_pending_remove(cmd);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001920}
1921
1922static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1923{
1924 struct pending_cmd *cmd;
1925
1926 BT_DBG("status %u", status);
1927
Johan Hedberg56e5cb82011-11-08 20:40:16 +02001928 cmd = find_pairing(conn);
1929 if (!cmd)
1930 BT_DBG("Unable to find a pending command");
1931 else
Johan Hedberge2113262012-02-18 15:20:03 +02001932 pairing_complete(cmd, mgmt_status(status));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001933}
1934
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001935static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
1936 u16 len)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001937{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001938 struct mgmt_cp_pair_device *cp = data;
Johan Hedberg1425acb2011-11-11 00:07:35 +02001939 struct mgmt_rp_pair_device rp;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001940 struct pending_cmd *cmd;
1941 u8 sec_level, auth_type;
1942 struct hci_conn *conn;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001943 int err;
1944
1945 BT_DBG("");
1946
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001947 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001948 return cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001949 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001950
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001951 hci_dev_lock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001952
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02001953 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001954 err = cmd_status(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02001955 MGMT_STATUS_NOT_POWERED);
1956 goto unlock;
1957 }
1958
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001959 sec_level = BT_SECURITY_MEDIUM;
1960 if (cp->io_cap == 0x03)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001961 auth_type = HCI_AT_DEDICATED_BONDING;
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001962 else
Johan Hedberge9a416b2011-02-19 12:05:56 -03001963 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001964
Johan Hedbergba4e5642011-11-11 00:07:34 +02001965 if (cp->addr.type == MGMT_ADDR_BREDR)
1966 conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001967 auth_type);
1968 else
Johan Hedbergba4e5642011-11-11 00:07:34 +02001969 conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001970 auth_type);
1971
Johan Hedberg1425acb2011-11-11 00:07:35 +02001972 memset(&rp, 0, sizeof(rp));
1973 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1974 rp.addr.type = cp->addr.type;
1975
Ville Tervo30e76272011-02-22 16:10:53 -03001976 if (IS_ERR(conn)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001977 err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
Johan Hedberge2113262012-02-18 15:20:03 +02001978 MGMT_STATUS_CONNECT_FAILED,
1979 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001980 goto unlock;
1981 }
1982
1983 if (conn->connect_cfm_cb) {
1984 hci_conn_put(conn);
Johan Hedbergbdb6d972012-02-28 06:13:32 +02001985 err = cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE,
Johan Hedberge2113262012-02-18 15:20:03 +02001986 MGMT_STATUS_BUSY, &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001987 goto unlock;
1988 }
1989
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001990 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001991 if (!cmd) {
1992 err = -ENOMEM;
1993 hci_conn_put(conn);
1994 goto unlock;
1995 }
1996
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001997 /* For LE, just connecting isn't a proof that the pairing finished */
Johan Hedbergba4e5642011-11-11 00:07:34 +02001998 if (cp->addr.type == MGMT_ADDR_BREDR)
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001999 conn->connect_cfm_cb = pairing_complete_cb;
2000
Johan Hedberge9a416b2011-02-19 12:05:56 -03002001 conn->security_cfm_cb = pairing_complete_cb;
2002 conn->disconn_cfm_cb = pairing_complete_cb;
2003 conn->io_capability = cp->io_cap;
2004 cmd->user_data = conn;
2005
2006 if (conn->state == BT_CONNECTED &&
2007 hci_conn_security(conn, sec_level, auth_type))
2008 pairing_complete(cmd, 0);
2009
2010 err = 0;
2011
2012unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002013 hci_dev_unlock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002014 return err;
2015}
2016
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002017static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev,
Johan Hedberg28424702012-02-02 04:02:29 +02002018 unsigned char *data, u16 len)
2019{
2020 struct mgmt_addr_info *addr = (void *) data;
Johan Hedberg28424702012-02-02 04:02:29 +02002021 struct pending_cmd *cmd;
2022 struct hci_conn *conn;
2023 int err;
2024
2025 BT_DBG("");
2026
2027 if (len != sizeof(*addr))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002028 return cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
Johan Hedberg28424702012-02-02 04:02:29 +02002029 MGMT_STATUS_INVALID_PARAMS);
2030
2031 hci_dev_lock(hdev);
2032
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002033 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002034 err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002035 MGMT_STATUS_NOT_POWERED);
2036 goto unlock;
2037 }
2038
Johan Hedberg28424702012-02-02 04:02:29 +02002039 cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
2040 if (!cmd) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002041 err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
Johan Hedberg28424702012-02-02 04:02:29 +02002042 MGMT_STATUS_INVALID_PARAMS);
2043 goto unlock;
2044 }
2045
2046 conn = cmd->user_data;
2047
2048 if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002049 err = cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE,
Johan Hedberg28424702012-02-02 04:02:29 +02002050 MGMT_STATUS_INVALID_PARAMS);
2051 goto unlock;
2052 }
2053
2054 pairing_complete(cmd, MGMT_STATUS_CANCELLED);
2055
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002056 err = cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
2057 addr, sizeof(*addr));
Johan Hedberg28424702012-02-02 04:02:29 +02002058unlock:
2059 hci_dev_unlock(hdev);
Johan Hedberg28424702012-02-02 04:02:29 +02002060 return err;
2061}
2062
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002063static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
2064 bdaddr_t *bdaddr, u8 type, u16 mgmt_op,
2065 u16 hci_op, __le32 passkey)
Johan Hedberga5c29682011-02-19 12:05:57 -03002066{
Johan Hedberga5c29682011-02-19 12:05:57 -03002067 struct pending_cmd *cmd;
Brian Gix0df4c182011-11-16 13:53:13 -08002068 struct hci_conn *conn;
Johan Hedberga5c29682011-02-19 12:05:57 -03002069 int err;
2070
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002071 hci_dev_lock(hdev);
Johan Hedberg08ba5382011-03-16 14:29:34 +02002072
Johan Hedberg4b34ee782012-02-21 14:13:02 +02002073 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002074 err = cmd_status(sk, hdev->id, mgmt_op,
2075 MGMT_STATUS_NOT_POWERED);
Brian Gix0df4c182011-11-16 13:53:13 -08002076 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03002077 }
2078
Johan Hedberg272d90d2012-02-09 15:26:12 +02002079 if (type == MGMT_ADDR_BREDR)
2080 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
2081 else
Brian Gix47c15e22011-11-16 13:53:14 -08002082 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
Brian Gix47c15e22011-11-16 13:53:14 -08002083
Johan Hedberg272d90d2012-02-09 15:26:12 +02002084 if (!conn) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002085 err = cmd_status(sk, hdev->id, mgmt_op,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002086 MGMT_STATUS_NOT_CONNECTED);
2087 goto done;
2088 }
2089
2090 if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
Brian Gix47c15e22011-11-16 13:53:14 -08002091 /* Continue with pairing via SMP */
Brian Gix5fe57d92011-12-21 16:12:13 -08002092 err = smp_user_confirm_reply(conn, mgmt_op, passkey);
Brian Gix47c15e22011-11-16 13:53:14 -08002093
Brian Gix5fe57d92011-12-21 16:12:13 -08002094 if (!err)
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002095 err = cmd_status(sk, hdev->id, mgmt_op,
Brian Gix5fe57d92011-12-21 16:12:13 -08002096 MGMT_STATUS_SUCCESS);
2097 else
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002098 err = cmd_status(sk, hdev->id, mgmt_op,
Brian Gix5fe57d92011-12-21 16:12:13 -08002099 MGMT_STATUS_FAILED);
2100
Brian Gix47c15e22011-11-16 13:53:14 -08002101 goto done;
2102 }
2103
Brian Gix0df4c182011-11-16 13:53:13 -08002104 cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
Johan Hedberga5c29682011-02-19 12:05:57 -03002105 if (!cmd) {
2106 err = -ENOMEM;
Brian Gix0df4c182011-11-16 13:53:13 -08002107 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03002108 }
2109
Brian Gix0df4c182011-11-16 13:53:13 -08002110 /* Continue with pairing via HCI */
Brian Gix604086b2011-11-23 08:28:33 -08002111 if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
2112 struct hci_cp_user_passkey_reply cp;
2113
2114 bacpy(&cp.bdaddr, bdaddr);
2115 cp.passkey = passkey;
2116 err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
2117 } else
2118 err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
2119
Johan Hedberga664b5b2011-02-19 12:06:02 -03002120 if (err < 0)
2121 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03002122
Brian Gix0df4c182011-11-16 13:53:13 -08002123done:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002124 hci_dev_unlock(hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03002125 return err;
2126}
2127
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002128static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev,
2129 void *data, u16 len)
Brian Gix0df4c182011-11-16 13:53:13 -08002130{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002131 struct mgmt_cp_user_confirm_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08002132
2133 BT_DBG("");
2134
2135 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002136 return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY,
Brian Gix0df4c182011-11-16 13:53:13 -08002137 MGMT_STATUS_INVALID_PARAMS);
2138
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002139 return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002140 MGMT_OP_USER_CONFIRM_REPLY,
2141 HCI_OP_USER_CONFIRM_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08002142}
2143
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002144static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev,
2145 void *data, u16 len)
Brian Gix0df4c182011-11-16 13:53:13 -08002146{
Johan Hedbergc9c26592011-12-15 00:47:41 +02002147 struct mgmt_cp_user_confirm_neg_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08002148
2149 BT_DBG("");
2150
2151 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002152 return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_NEG_REPLY,
Brian Gix0df4c182011-11-16 13:53:13 -08002153 MGMT_STATUS_INVALID_PARAMS);
2154
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002155 return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002156 MGMT_OP_USER_CONFIRM_NEG_REPLY,
2157 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08002158}
2159
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002160static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev,
2161 void *data, u16 len)
Brian Gix604086b2011-11-23 08:28:33 -08002162{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002163 struct mgmt_cp_user_passkey_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08002164
2165 BT_DBG("");
2166
2167 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002168 return cmd_status(sk, hdev->id, MGMT_OP_USER_PASSKEY_REPLY,
Brian Gix604086b2011-11-23 08:28:33 -08002169 EINVAL);
2170
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002171 return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002172 MGMT_OP_USER_PASSKEY_REPLY,
2173 HCI_OP_USER_PASSKEY_REPLY,
2174 cp->passkey);
Brian Gix604086b2011-11-23 08:28:33 -08002175}
2176
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002177static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev,
2178 void *data, u16 len)
Brian Gix604086b2011-11-23 08:28:33 -08002179{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002180 struct mgmt_cp_user_passkey_neg_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08002181
2182 BT_DBG("");
2183
2184 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002185 return cmd_status(sk, hdev->id, MGMT_OP_USER_PASSKEY_NEG_REPLY,
2186 EINVAL);
Brian Gix604086b2011-11-23 08:28:33 -08002187
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002188 return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
Johan Hedberg272d90d2012-02-09 15:26:12 +02002189 MGMT_OP_USER_PASSKEY_NEG_REPLY,
2190 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
Brian Gix604086b2011-11-23 08:28:33 -08002191}
2192
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002193static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data,
Johan Hedbergb312b1612011-03-16 14:29:37 +02002194 u16 len)
2195{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002196 struct mgmt_cp_set_local_name *mgmt_cp = data;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002197 struct hci_cp_write_local_name hci_cp;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002198 struct pending_cmd *cmd;
2199 int err;
2200
2201 BT_DBG("");
2202
2203 if (len != sizeof(*mgmt_cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002204 return cmd_status(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02002205 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002206
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002207 hci_dev_lock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002208
Johan Hedberg28cc7bd2012-02-22 21:06:55 +02002209 memcpy(hdev->short_name, mgmt_cp->short_name,
2210 sizeof(hdev->short_name));
2211
Johan Hedbergb5235a62012-02-21 14:32:24 +02002212 if (!hdev_is_powered(hdev)) {
Johan Hedberg28cc7bd2012-02-22 21:06:55 +02002213 memcpy(hdev->dev_name, mgmt_cp->name, sizeof(hdev->dev_name));
2214
2215 err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0,
2216 data, len);
2217 if (err < 0)
2218 goto failed;
2219
2220 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, len,
2221 sk);
2222
Johan Hedbergb5235a62012-02-21 14:32:24 +02002223 goto failed;
2224 }
2225
Johan Hedberg28cc7bd2012-02-22 21:06:55 +02002226 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002227 if (!cmd) {
2228 err = -ENOMEM;
2229 goto failed;
2230 }
2231
2232 memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
2233 err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
2234 &hci_cp);
2235 if (err < 0)
2236 mgmt_pending_remove(cmd);
2237
2238failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002239 hci_dev_unlock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002240 return err;
2241}
2242
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002243static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev)
Szymon Jancc35938b2011-03-22 13:12:21 +01002244{
Szymon Jancc35938b2011-03-22 13:12:21 +01002245 struct pending_cmd *cmd;
2246 int err;
2247
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002248 BT_DBG("%s", hdev->name);
Szymon Jancc35938b2011-03-22 13:12:21 +01002249
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002250 hci_dev_lock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002251
Johan Hedberg4b34ee782012-02-21 14:13:02 +02002252 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002253 err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002254 MGMT_STATUS_NOT_POWERED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002255 goto unlock;
2256 }
2257
2258 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002259 err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002260 MGMT_STATUS_NOT_SUPPORTED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002261 goto unlock;
2262 }
2263
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002264 if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002265 err = cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002266 MGMT_STATUS_BUSY);
Szymon Jancc35938b2011-03-22 13:12:21 +01002267 goto unlock;
2268 }
2269
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002270 cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
Szymon Jancc35938b2011-03-22 13:12:21 +01002271 if (!cmd) {
2272 err = -ENOMEM;
2273 goto unlock;
2274 }
2275
2276 err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
2277 if (err < 0)
2278 mgmt_pending_remove(cmd);
2279
2280unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002281 hci_dev_unlock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002282 return err;
2283}
2284
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002285static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
2286 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002287{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002288 struct mgmt_cp_add_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002289 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002290 int err;
2291
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002292 BT_DBG("%s ", hdev->name);
Szymon Janc2763eda2011-03-22 13:12:22 +01002293
2294 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002295 return cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002296 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002297
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002298 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002299
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002300 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002301 err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002302 MGMT_STATUS_NOT_POWERED,
2303 &cp->addr, sizeof(cp->addr));
2304 goto unlock;
2305 }
2306
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002307 err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
Szymon Janc2763eda2011-03-22 13:12:22 +01002308 cp->randomizer);
2309 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002310 status = MGMT_STATUS_FAILED;
Szymon Janc2763eda2011-03-22 13:12:22 +01002311 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002312 status = 0;
2313
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002314 err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, status,
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002315 &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002316
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002317unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002318 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002319 return err;
2320}
2321
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002322static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002323 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002324{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002325 struct mgmt_cp_remove_remote_oob_data *cp = data;
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002326 u8 status;
Szymon Janc2763eda2011-03-22 13:12:22 +01002327 int err;
2328
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002329 BT_DBG("%s", hdev->name);
Szymon Janc2763eda2011-03-22 13:12:22 +01002330
2331 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002332 return cmd_status(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002333 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002334
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002335 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002336
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002337 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002338 err = cmd_complete(sk, hdev->id,
2339 MGMT_OP_REMOVE_REMOTE_OOB_DATA,
2340 MGMT_STATUS_NOT_POWERED,
2341 &cp->addr, sizeof(cp->addr));
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002342 goto unlock;
2343 }
2344
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002345 err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
Szymon Janc2763eda2011-03-22 13:12:22 +01002346 if (err < 0)
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002347 status = MGMT_STATUS_INVALID_PARAMS;
Szymon Janc2763eda2011-03-22 13:12:22 +01002348 else
Johan Hedbergbf1e3542012-02-19 13:16:14 +02002349 status = 0;
2350
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002351 err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
2352 status, &cp->addr, sizeof(cp->addr));
Szymon Janc2763eda2011-03-22 13:12:22 +01002353
Johan Hedberg5f97c1d2012-02-22 22:41:18 +02002354unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002355 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002356 return err;
2357}
2358
Andre Guedes5e0452c2012-02-17 20:39:38 -03002359int mgmt_interleaved_discovery(struct hci_dev *hdev)
2360{
2361 int err;
2362
2363 BT_DBG("%s", hdev->name);
2364
2365 hci_dev_lock(hdev);
2366
2367 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
2368 if (err < 0)
2369 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2370
2371 hci_dev_unlock(hdev);
2372
2373 return err;
2374}
2375
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002376static int start_discovery(struct sock *sk, struct hci_dev *hdev,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002377 void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002378{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002379 struct mgmt_cp_start_discovery *cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002380 struct pending_cmd *cmd;
Johan Hedberg14a53662011-04-27 10:29:56 -04002381 int err;
2382
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002383 BT_DBG("%s", hdev->name);
Johan Hedberg14a53662011-04-27 10:29:56 -04002384
Johan Hedberg450dfda2011-11-12 11:58:22 +02002385 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002386 return cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
Johan Hedbergca69b792011-11-11 18:10:00 +02002387 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002388
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002389 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002390
Johan Hedberg4b34ee782012-02-21 14:13:02 +02002391 if (!hdev_is_powered(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002392 err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
Johan Hedbergca69b792011-11-11 18:10:00 +02002393 MGMT_STATUS_NOT_POWERED);
Johan Hedbergbd2d1332011-11-07 23:13:37 +02002394 goto failed;
2395 }
2396
Johan Hedbergff9ef572012-01-04 14:23:45 +02002397 if (hdev->discovery.state != DISCOVERY_STOPPED) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002398 err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
2399 MGMT_STATUS_BUSY);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002400 goto failed;
2401 }
2402
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002403 cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002404 if (!cmd) {
2405 err = -ENOMEM;
2406 goto failed;
2407 }
2408
Andre Guedes4aab14e2012-02-17 20:39:36 -03002409 hdev->discovery.type = cp->type;
2410
2411 switch (hdev->discovery.type) {
Andre Guedesf39799f2012-02-17 20:39:35 -03002412 case DISCOV_TYPE_BREDR:
Andre Guedes8b901292012-02-23 18:09:27 -03002413 if (lmp_bredr_capable(hdev))
2414 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
2415 else
2416 err = -ENOTSUPP;
Andre Guedesf39799f2012-02-17 20:39:35 -03002417 break;
2418
2419 case DISCOV_TYPE_LE:
Andre Guedes8b901292012-02-23 18:09:27 -03002420 if (lmp_host_le_capable(hdev))
2421 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
Andre Guedes3fd24152012-02-03 17:48:01 -03002422 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
Andre Guedes8b901292012-02-23 18:09:27 -03002423 else
2424 err = -ENOTSUPP;
Andre Guedesf39799f2012-02-17 20:39:35 -03002425 break;
2426
Andre Guedes5e0452c2012-02-17 20:39:38 -03002427 case DISCOV_TYPE_INTERLEAVED:
Andre Guedes426c1892012-02-24 11:41:04 -03002428 if (lmp_host_le_capable(hdev) && lmp_bredr_capable(hdev))
2429 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
2430 LE_SCAN_WIN, LE_SCAN_TIMEOUT_BREDR_LE);
2431 else
2432 err = -ENOTSUPP;
Andre Guedes5e0452c2012-02-17 20:39:38 -03002433 break;
2434
Andre Guedesf39799f2012-02-17 20:39:35 -03002435 default:
Andre Guedes3fd24152012-02-03 17:48:01 -03002436 err = -EINVAL;
Andre Guedesf39799f2012-02-17 20:39:35 -03002437 }
Andre Guedes3fd24152012-02-03 17:48:01 -03002438
Johan Hedberg14a53662011-04-27 10:29:56 -04002439 if (err < 0)
2440 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002441 else
2442 hci_discovery_set_state(hdev, DISCOVERY_STARTING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002443
2444failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002445 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002446 return err;
2447}
2448
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002449static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
2450 u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002451{
Johan Hedbergd9306502012-02-20 23:25:18 +02002452 struct mgmt_cp_stop_discovery *mgmt_cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002453 struct pending_cmd *cmd;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002454 struct hci_cp_remote_name_req_cancel cp;
2455 struct inquiry_entry *e;
Johan Hedberg14a53662011-04-27 10:29:56 -04002456 int err;
2457
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002458 BT_DBG("%s", hdev->name);
Johan Hedberg14a53662011-04-27 10:29:56 -04002459
Johan Hedbergd9306502012-02-20 23:25:18 +02002460 if (len != sizeof(*mgmt_cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002461 return cmd_status(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
Johan Hedbergca69b792011-11-11 18:10:00 +02002462 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002463
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002464 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002465
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002466 if (!hci_discovery_active(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002467 err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
Johan Hedbergd9306502012-02-20 23:25:18 +02002468 MGMT_STATUS_REJECTED,
2469 &mgmt_cp->type, sizeof(mgmt_cp->type));
2470 goto unlock;
2471 }
2472
2473 if (hdev->discovery.type != mgmt_cp->type) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002474 err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY,
Johan Hedbergd9306502012-02-20 23:25:18 +02002475 MGMT_STATUS_INVALID_PARAMS,
2476 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002477 goto unlock;
Johan Hedbergff9ef572012-01-04 14:23:45 +02002478 }
2479
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002480 cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002481 if (!cmd) {
2482 err = -ENOMEM;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002483 goto unlock;
Johan Hedberg14a53662011-04-27 10:29:56 -04002484 }
2485
Andre Guedes343f9352012-02-17 20:39:37 -03002486 if (hdev->discovery.state == DISCOVERY_FINDING) {
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002487 err = hci_cancel_inquiry(hdev);
2488 if (err < 0)
2489 mgmt_pending_remove(cmd);
2490 else
2491 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
2492 goto unlock;
2493 }
2494
2495 e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
2496 if (!e) {
2497 mgmt_pending_remove(cmd);
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002498 err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0,
Johan Hedbergd9306502012-02-20 23:25:18 +02002499 &mgmt_cp->type, sizeof(mgmt_cp->type));
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002500 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2501 goto unlock;
2502 }
2503
2504 bacpy(&cp.bdaddr, &e->data.bdaddr);
2505 err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
2506 sizeof(cp), &cp);
Johan Hedberg14a53662011-04-27 10:29:56 -04002507 if (err < 0)
2508 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002509 else
2510 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002511
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002512unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002513 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002514 return err;
2515}
2516
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002517static int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data,
2518 u16 len)
Johan Hedberg561aafb2012-01-04 13:31:59 +02002519{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002520 struct mgmt_cp_confirm_name *cp = data;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002521 struct inquiry_entry *e;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002522 int err;
2523
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002524 BT_DBG("%s", hdev->name);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002525
2526 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002527 return cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
2528 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002529
2530 hci_dev_lock(hdev);
2531
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002532 if (!hci_discovery_active(hdev)) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002533 err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002534 MGMT_STATUS_FAILED);
2535 goto failed;
2536 }
2537
Johan Hedberga198e7b2012-02-17 14:27:06 +02002538 e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002539 if (!e) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002540 err = cmd_status(sk, hdev->id, MGMT_OP_CONFIRM_NAME,
2541 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002542 goto failed;
2543 }
2544
2545 if (cp->name_known) {
2546 e->name_state = NAME_KNOWN;
2547 list_del(&e->list);
2548 } else {
2549 e->name_state = NAME_NEEDED;
Johan Hedberga3d4e202012-01-09 00:53:02 +02002550 hci_inquiry_cache_update_resolve(hdev, e);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002551 }
2552
2553 err = 0;
2554
2555failed:
2556 hci_dev_unlock(hdev);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002557 return err;
2558}
2559
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002560static int block_device(struct sock *sk, struct hci_dev *hdev, void *data,
2561 u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002562{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002563 struct mgmt_cp_block_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002564 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002565 int err;
2566
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002567 BT_DBG("%s", hdev->name);
Antti Julku7fbec222011-06-15 12:01:15 +03002568
Antti Julku7fbec222011-06-15 12:01:15 +03002569 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002570 return cmd_status(sk, hdev->id, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002571 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002572
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002573 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002574
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002575 err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002576 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002577 status = MGMT_STATUS_FAILED;
Antti Julku7fbec222011-06-15 12:01:15 +03002578 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002579 status = 0;
2580
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002581 err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status,
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002582 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002583
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002584 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002585
2586 return err;
2587}
2588
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002589static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data,
2590 u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002591{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002592 struct mgmt_cp_unblock_device *cp = data;
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002593 u8 status;
Antti Julku7fbec222011-06-15 12:01:15 +03002594 int err;
2595
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002596 BT_DBG("%s", hdev->name);
Antti Julku7fbec222011-06-15 12:01:15 +03002597
Antti Julku7fbec222011-06-15 12:01:15 +03002598 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002599 return cmd_status(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002600 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002601
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002602 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002603
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002604 err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002605 if (err < 0)
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002606 status = MGMT_STATUS_INVALID_PARAMS;
Antti Julku7fbec222011-06-15 12:01:15 +03002607 else
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002608 status = 0;
2609
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002610 err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status,
Johan Hedbergf0eeea82012-02-19 12:58:54 +02002611 &cp->addr, sizeof(cp->addr));
Antti Julku5e762442011-08-25 16:48:02 +03002612
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002613 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002614
2615 return err;
2616}
2617
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002618static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
2619 void *data, u16 len)
Antti Julkuf6422ec2011-06-22 13:11:56 +03002620{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002621 struct mgmt_mode *cp = data;
Antti Julkuf6422ec2011-06-22 13:11:56 +03002622 struct hci_cp_write_page_scan_activity acp;
2623 u8 type;
2624 int err;
2625
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002626 BT_DBG("%s", hdev->name);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002627
2628 if (len != sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002629 return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002630 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002631
Johan Hedberg5400c042012-02-21 16:40:33 +02002632 if (!hdev_is_powered(hdev))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002633 return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedberg5400c042012-02-21 16:40:33 +02002634 MGMT_STATUS_NOT_POWERED);
2635
2636 if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002637 return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
2638 MGMT_STATUS_REJECTED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002639
2640 hci_dev_lock(hdev);
2641
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002642 if (cp->val) {
Antti Julkuf6422ec2011-06-22 13:11:56 +03002643 type = PAGE_SCAN_TYPE_INTERLACED;
2644 acp.interval = 0x0024; /* 22.5 msec page scan interval */
2645 } else {
2646 type = PAGE_SCAN_TYPE_STANDARD; /* default */
2647 acp.interval = 0x0800; /* default 1.28 sec page scan */
2648 }
2649
2650 acp.window = 0x0012; /* default 11.25 msec page scan window */
2651
2652 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
2653 sizeof(acp), &acp);
2654 if (err < 0) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002655 err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
2656 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002657 goto done;
2658 }
2659
2660 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
2661 if (err < 0) {
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002662 err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE,
2663 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002664 goto done;
2665 }
2666
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002667 err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0,
Johan Hedbergaee9b212012-02-18 15:07:59 +02002668 NULL, 0);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002669done:
2670 hci_dev_unlock(hdev);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002671 return err;
2672}
2673
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002674static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002675 void *cp_data, u16 len)
2676{
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002677 struct mgmt_cp_load_long_term_keys *cp = cp_data;
2678 u16 key_count, expected_len;
2679 int i;
2680
2681 if (len < sizeof(*cp))
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002682 return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002683 EINVAL);
2684
2685 key_count = get_unaligned_le16(&cp->key_count);
2686
2687 expected_len = sizeof(*cp) + key_count *
2688 sizeof(struct mgmt_ltk_info);
2689 if (expected_len != len) {
2690 BT_ERR("load_keys: expected %u bytes, got %u bytes",
2691 len, expected_len);
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002692 return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS,
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002693 EINVAL);
2694 }
2695
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002696 BT_DBG("%s key_count %u", hdev->name, key_count);
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002697
2698 hci_dev_lock(hdev);
2699
2700 hci_smp_ltks_clear(hdev);
2701
2702 for (i = 0; i < key_count; i++) {
2703 struct mgmt_ltk_info *key = &cp->keys[i];
2704 u8 type;
2705
2706 if (key->master)
2707 type = HCI_SMP_LTK;
2708 else
2709 type = HCI_SMP_LTK_SLAVE;
2710
2711 hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
2712 type, 0, key->authenticated, key->val,
2713 key->enc_size, key->ediv, key->rand);
2714 }
2715
2716 hci_dev_unlock(hdev);
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002717
2718 return 0;
2719}
2720
Johan Hedberg03811012010-12-08 00:21:06 +02002721int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
2722{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002723 void *buf;
2724 u8 *cp;
Johan Hedberg03811012010-12-08 00:21:06 +02002725 struct mgmt_hdr *hdr;
Szymon Janc4e51eae2011-02-25 19:05:48 +01002726 u16 opcode, index, len;
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002727 struct hci_dev *hdev = NULL;
Johan Hedberg03811012010-12-08 00:21:06 +02002728 int err;
2729
2730 BT_DBG("got %zu bytes", msglen);
2731
2732 if (msglen < sizeof(*hdr))
2733 return -EINVAL;
2734
Gustavo F. Padovane63a15e2011-04-04 18:56:53 -03002735 buf = kmalloc(msglen, GFP_KERNEL);
Johan Hedberg03811012010-12-08 00:21:06 +02002736 if (!buf)
2737 return -ENOMEM;
2738
2739 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
2740 err = -EFAULT;
2741 goto done;
2742 }
2743
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002744 hdr = buf;
Johan Hedberg03811012010-12-08 00:21:06 +02002745 opcode = get_unaligned_le16(&hdr->opcode);
Szymon Janc4e51eae2011-02-25 19:05:48 +01002746 index = get_unaligned_le16(&hdr->index);
Johan Hedberg03811012010-12-08 00:21:06 +02002747 len = get_unaligned_le16(&hdr->len);
2748
2749 if (len != msglen - sizeof(*hdr)) {
2750 err = -EINVAL;
2751 goto done;
2752 }
2753
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002754 if (opcode < MGMT_OP_READ_INFO) {
2755 if (index != MGMT_INDEX_NONE) {
2756 err = cmd_status(sk, index, opcode,
2757 MGMT_STATUS_INVALID_PARAMS);
2758 goto done;
2759 }
2760 } else {
2761 hdev = hci_dev_get(index);
2762 if (!hdev) {
2763 err = cmd_status(sk, index, opcode,
2764 MGMT_STATUS_INVALID_PARAMS);
2765 goto done;
2766 }
2767 }
2768
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002769 cp = buf + sizeof(*hdr);
2770
Johan Hedberg03811012010-12-08 00:21:06 +02002771 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +02002772 case MGMT_OP_READ_VERSION:
2773 err = read_version(sk);
2774 break;
Johan Hedberge70bb2e2012-02-13 16:59:33 +02002775 case MGMT_OP_READ_COMMANDS:
2776 err = read_commands(sk);
2777 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +02002778 case MGMT_OP_READ_INDEX_LIST:
2779 err = read_index_list(sk);
2780 break;
Johan Hedbergf7b64e692010-12-13 21:07:06 +02002781 case MGMT_OP_READ_INFO:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002782 err = read_controller_info(sk, hdev);
Johan Hedbergf7b64e692010-12-13 21:07:06 +02002783 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002784 case MGMT_OP_SET_POWERED:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002785 err = set_powered(sk, hdev, cp, len);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002786 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +02002787 case MGMT_OP_SET_DISCOVERABLE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002788 err = set_discoverable(sk, hdev, cp, len);
Johan Hedberg73f22f62010-12-29 16:00:25 +02002789 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002790 case MGMT_OP_SET_CONNECTABLE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002791 err = set_connectable(sk, hdev, cp, len);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002792 break;
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002793 case MGMT_OP_SET_FAST_CONNECTABLE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002794 err = set_fast_connectable(sk, hdev, cp, len);
Johan Hedbergf7c6869ce2011-12-15 00:47:36 +02002795 break;
Johan Hedbergc542a062011-01-26 13:11:03 +02002796 case MGMT_OP_SET_PAIRABLE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002797 err = set_pairable(sk, hdev, cp, len);
Johan Hedbergc542a062011-01-26 13:11:03 +02002798 break;
Johan Hedberg33ef95e2012-02-16 23:56:27 +02002799 case MGMT_OP_SET_LINK_SECURITY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002800 err = set_link_security(sk, hdev, cp, len);
Johan Hedberg33ef95e2012-02-16 23:56:27 +02002801 break;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02002802 case MGMT_OP_SET_SSP:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002803 err = set_ssp(sk, hdev, cp, len);
Johan Hedberged2c4ee2012-02-17 00:56:28 +02002804 break;
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02002805 case MGMT_OP_SET_HS:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002806 err = set_hs(sk, hdev, cp, len);
Johan Hedberg6d80dfd2012-02-20 23:50:38 +02002807 break;
Johan Hedberg06199cf2012-02-22 16:37:11 +02002808 case MGMT_OP_SET_LE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002809 err = set_le(sk, hdev, cp, len);
Johan Hedberg06199cf2012-02-22 16:37:11 +02002810 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002811 case MGMT_OP_ADD_UUID:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002812 err = add_uuid(sk, hdev, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002813 break;
2814 case MGMT_OP_REMOVE_UUID:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002815 err = remove_uuid(sk, hdev, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002816 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002817 case MGMT_OP_SET_DEV_CLASS:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002818 err = set_dev_class(sk, hdev, cp, len);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002819 break;
Johan Hedberg86742e12011-11-07 23:13:38 +02002820 case MGMT_OP_LOAD_LINK_KEYS:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002821 err = load_link_keys(sk, hdev, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002822 break;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002823 case MGMT_OP_DISCONNECT:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002824 err = disconnect(sk, hdev, cp, len);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002825 break;
Johan Hedberg2784eb42011-01-21 13:56:35 +02002826 case MGMT_OP_GET_CONNECTIONS:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002827 err = get_connections(sk, hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02002828 break;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002829 case MGMT_OP_PIN_CODE_REPLY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002830 err = pin_code_reply(sk, hdev, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002831 break;
2832 case MGMT_OP_PIN_CODE_NEG_REPLY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002833 err = pin_code_neg_reply(sk, hdev, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002834 break;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002835 case MGMT_OP_SET_IO_CAPABILITY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002836 err = set_io_capability(sk, hdev, cp, len);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002837 break;
Johan Hedberge9a416b2011-02-19 12:05:56 -03002838 case MGMT_OP_PAIR_DEVICE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002839 err = pair_device(sk, hdev, cp, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002840 break;
Johan Hedberg28424702012-02-02 04:02:29 +02002841 case MGMT_OP_CANCEL_PAIR_DEVICE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002842 err = cancel_pair_device(sk, hdev, buf + sizeof(*hdr), len);
Johan Hedberg28424702012-02-02 04:02:29 +02002843 break;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002844 case MGMT_OP_UNPAIR_DEVICE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002845 err = unpair_device(sk, hdev, cp, len);
Johan Hedberg124f6e32012-02-09 13:50:12 +02002846 break;
Johan Hedberga5c29682011-02-19 12:05:57 -03002847 case MGMT_OP_USER_CONFIRM_REPLY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002848 err = user_confirm_reply(sk, hdev, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002849 break;
2850 case MGMT_OP_USER_CONFIRM_NEG_REPLY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002851 err = user_confirm_neg_reply(sk, hdev, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002852 break;
Brian Gix604086b2011-11-23 08:28:33 -08002853 case MGMT_OP_USER_PASSKEY_REPLY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002854 err = user_passkey_reply(sk, hdev, cp, len);
Brian Gix604086b2011-11-23 08:28:33 -08002855 break;
2856 case MGMT_OP_USER_PASSKEY_NEG_REPLY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002857 err = user_passkey_neg_reply(sk, hdev, cp, len);
Johan Hedberg55ed8ca12011-01-17 14:41:05 +02002858 break;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002859 case MGMT_OP_SET_LOCAL_NAME:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002860 err = set_local_name(sk, hdev, cp, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002861 break;
Szymon Jancc35938b2011-03-22 13:12:21 +01002862 case MGMT_OP_READ_LOCAL_OOB_DATA:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002863 err = read_local_oob_data(sk, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002864 break;
Szymon Janc2763eda2011-03-22 13:12:22 +01002865 case MGMT_OP_ADD_REMOTE_OOB_DATA:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002866 err = add_remote_oob_data(sk, hdev, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002867 break;
2868 case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002869 err = remove_remote_oob_data(sk, hdev, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002870 break;
Johan Hedberg14a53662011-04-27 10:29:56 -04002871 case MGMT_OP_START_DISCOVERY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002872 err = start_discovery(sk, hdev, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002873 break;
2874 case MGMT_OP_STOP_DISCOVERY:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002875 err = stop_discovery(sk, hdev, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002876 break;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002877 case MGMT_OP_CONFIRM_NAME:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002878 err = confirm_name(sk, hdev, cp, len);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002879 break;
Antti Julku7fbec222011-06-15 12:01:15 +03002880 case MGMT_OP_BLOCK_DEVICE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002881 err = block_device(sk, hdev, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002882 break;
2883 case MGMT_OP_UNBLOCK_DEVICE:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002884 err = unblock_device(sk, hdev, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002885 break;
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002886 case MGMT_OP_LOAD_LONG_TERM_KEYS:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002887 err = load_long_term_keys(sk, hdev, cp, len);
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002888 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002889 default:
Johan Hedberg72a734e2010-12-30 00:38:22 +02002890 BT_DBG("Unknown op %u", opcode);
Johan Hedbergca69b792011-11-11 18:10:00 +02002891 err = cmd_status(sk, index, opcode,
2892 MGMT_STATUS_UNKNOWN_COMMAND);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002893 break;
2894 }
Johan Hedberg72a734e2010-12-30 00:38:22 +02002895
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002896 if (err < 0)
Johan Hedberg72a734e2010-12-30 00:38:22 +02002897 goto done;
2898
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002899 err = msglen;
2900
2901done:
Johan Hedbergbdb6d972012-02-28 06:13:32 +02002902 if (hdev)
2903 hci_dev_put(hdev);
2904
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002905 kfree(buf);
2906 return err;
2907}
2908
Johan Hedbergb24752f2011-11-03 14:40:33 +02002909static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
2910{
2911 u8 *status = data;
2912
2913 cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
2914 mgmt_pending_remove(cmd);
2915}
2916
Johan Hedberg744cf192011-11-08 20:40:14 +02002917int mgmt_index_added(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002918{
Johan Hedberg744cf192011-11-08 20:40:14 +02002919 return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002920}
2921
Johan Hedberg744cf192011-11-08 20:40:14 +02002922int mgmt_index_removed(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002923{
Johan Hedbergb24752f2011-11-03 14:40:33 +02002924 u8 status = ENODEV;
2925
Johan Hedberg744cf192011-11-08 20:40:14 +02002926 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002927
Johan Hedberg744cf192011-11-08 20:40:14 +02002928 return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002929}
2930
2931struct cmd_lookup {
Johan Hedberg03811012010-12-08 00:21:06 +02002932 struct sock *sk;
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002933 struct hci_dev *hdev;
Johan Hedberg90e70452012-02-23 23:09:40 +02002934 u8 mgmt_status;
Johan Hedberg03811012010-12-08 00:21:06 +02002935};
2936
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002937static void settings_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberg03811012010-12-08 00:21:06 +02002938{
Johan Hedberg03811012010-12-08 00:21:06 +02002939 struct cmd_lookup *match = data;
2940
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002941 send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
Johan Hedberg03811012010-12-08 00:21:06 +02002942
2943 list_del(&cmd->list);
2944
2945 if (match->sk == NULL) {
2946 match->sk = cmd->sk;
2947 sock_hold(match->sk);
2948 }
2949
2950 mgmt_pending_free(cmd);
2951}
2952
Johan Hedberg744cf192011-11-08 20:40:14 +02002953int mgmt_powered(struct hci_dev *hdev, u8 powered)
Johan Hedberg03811012010-12-08 00:21:06 +02002954{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002955 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002956 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002957
Johan Hedberg5e5282b2012-02-21 16:01:30 +02002958 if (!test_bit(HCI_MGMT, &hdev->dev_flags))
2959 return 0;
2960
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002961 mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002962
Johan Hedberg5e5282b2012-02-21 16:01:30 +02002963 if (powered) {
2964 u8 scan = 0;
2965
2966 if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags))
2967 scan |= SCAN_PAGE;
2968 if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
2969 scan |= SCAN_INQUIRY;
2970
2971 if (scan)
2972 hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
Johan Hedberg504c8dc2012-02-23 13:30:41 +02002973
2974 update_class(hdev);
2975 update_eir(hdev);
Johan Hedberg5e5282b2012-02-21 16:01:30 +02002976 } else {
Johan Hedbergb24752f2011-11-03 14:40:33 +02002977 u8 status = ENETDOWN;
Johan Hedberg744cf192011-11-08 20:40:14 +02002978 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002979 }
2980
Johan Hedbergbeadb2b2012-02-21 16:55:31 +02002981 err = new_settings(hdev, match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002982
2983 if (match.sk)
2984 sock_put(match.sk);
2985
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002986 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002987}
2988
Johan Hedberg744cf192011-11-08 20:40:14 +02002989int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
Johan Hedberg03811012010-12-08 00:21:06 +02002990{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002991 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg5e5282b2012-02-21 16:01:30 +02002992 bool changed = false;
2993 int err = 0;
Johan Hedberg03811012010-12-08 00:21:06 +02002994
Johan Hedberg5e5282b2012-02-21 16:01:30 +02002995 if (discoverable) {
2996 if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
2997 changed = true;
2998 } else {
2999 if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
3000 changed = true;
3001 }
Johan Hedberg03811012010-12-08 00:21:06 +02003002
Johan Hedberged9b5f22012-02-21 20:47:06 +02003003 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp,
3004 &match);
3005
Johan Hedbergbeadb2b2012-02-21 16:55:31 +02003006 if (changed)
3007 err = new_settings(hdev, match.sk);
Johan Hedberg5e5282b2012-02-21 16:01:30 +02003008
Johan Hedberg03811012010-12-08 00:21:06 +02003009 if (match.sk)
3010 sock_put(match.sk);
3011
Johan Hedberg7bb895d2012-02-17 01:20:00 +02003012 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02003013}
3014
Johan Hedberg744cf192011-11-08 20:40:14 +02003015int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
Johan Hedberg03811012010-12-08 00:21:06 +02003016{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02003017 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg5e5282b2012-02-21 16:01:30 +02003018 bool changed = false;
3019 int err = 0;
Johan Hedberg03811012010-12-08 00:21:06 +02003020
Johan Hedberg5e5282b2012-02-21 16:01:30 +02003021 if (connectable) {
3022 if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags))
3023 changed = true;
3024 } else {
3025 if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags))
3026 changed = true;
3027 }
Johan Hedberg03811012010-12-08 00:21:06 +02003028
Johan Hedberged9b5f22012-02-21 20:47:06 +02003029 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
3030 &match);
3031
Johan Hedbergbeadb2b2012-02-21 16:55:31 +02003032 if (changed)
3033 err = new_settings(hdev, match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02003034
3035 if (match.sk)
3036 sock_put(match.sk);
3037
Johan Hedberg7bb895d2012-02-17 01:20:00 +02003038 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02003039}
3040
Johan Hedberg744cf192011-11-08 20:40:14 +02003041int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
Johan Hedberg2d7cee52011-11-07 22:16:03 +02003042{
Johan Hedbergca69b792011-11-11 18:10:00 +02003043 u8 mgmt_err = mgmt_status(status);
3044
Johan Hedberg2d7cee52011-11-07 22:16:03 +02003045 if (scan & SCAN_PAGE)
Johan Hedberg744cf192011-11-08 20:40:14 +02003046 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02003047 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02003048
3049 if (scan & SCAN_INQUIRY)
Johan Hedberg744cf192011-11-08 20:40:14 +02003050 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02003051 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02003052
3053 return 0;
3054}
3055
Johan Hedberg744cf192011-11-08 20:40:14 +02003056int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
3057 u8 persistent)
Johan Hedberg03811012010-12-08 00:21:06 +02003058{
Johan Hedberg86742e12011-11-07 23:13:38 +02003059 struct mgmt_ev_new_link_key ev;
Johan Hedberg03811012010-12-08 00:21:06 +02003060
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03003061 memset(&ev, 0, sizeof(ev));
Johan Hedberg03811012010-12-08 00:21:06 +02003062
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03003063 ev.store_hint = persistent;
Johan Hedbergd753fdc2012-02-17 14:06:34 +02003064 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
3065 ev.key.addr.type = MGMT_ADDR_BREDR;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03003066 ev.key.type = key->type;
3067 memcpy(ev.key.val, key->val, 16);
3068 ev.key.pin_len = key->pin_len;
Johan Hedberg03811012010-12-08 00:21:06 +02003069
Johan Hedberg744cf192011-11-08 20:40:14 +02003070 return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02003071}
Johan Hedbergf7520542011-01-20 12:34:39 +02003072
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03003073int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
3074{
3075 struct mgmt_ev_new_long_term_key ev;
3076
3077 memset(&ev, 0, sizeof(ev));
3078
3079 ev.store_hint = persistent;
3080 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
3081 ev.key.addr.type = key->bdaddr_type;
3082 ev.key.authenticated = key->authenticated;
3083 ev.key.enc_size = key->enc_size;
3084 ev.key.ediv = key->ediv;
3085
3086 if (key->type == HCI_SMP_LTK)
3087 ev.key.master = 1;
3088
3089 memcpy(ev.key.rand, key->rand, sizeof(key->rand));
3090 memcpy(ev.key.val, key->val, sizeof(key->val));
3091
3092 return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
3093 &ev, sizeof(ev), NULL);
3094}
3095
Johan Hedbergafc747a2012-01-15 18:11:07 +02003096int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg08c79b62012-02-23 22:31:51 +02003097 u8 addr_type, u32 flags, u8 *name,
3098 u8 name_len, u8 *dev_class)
Johan Hedbergf7520542011-01-20 12:34:39 +02003099{
Johan Hedbergb644ba32012-01-17 21:48:47 +02003100 char buf[512];
3101 struct mgmt_ev_device_connected *ev = (void *) buf;
3102 u16 eir_len = 0;
Johan Hedbergf7520542011-01-20 12:34:39 +02003103
Johan Hedbergb644ba32012-01-17 21:48:47 +02003104 bacpy(&ev->addr.bdaddr, bdaddr);
3105 ev->addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02003106
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02003107 ev->flags = __cpu_to_le32(flags);
Johan Hedberg08c79b62012-02-23 22:31:51 +02003108
Johan Hedbergb644ba32012-01-17 21:48:47 +02003109 if (name_len > 0)
3110 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
3111 name, name_len);
3112
3113 if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
3114 eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
3115 EIR_CLASS_OF_DEV, dev_class, 3);
3116
3117 put_unaligned_le16(eir_len, &ev->eir_len);
3118
3119 return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
3120 sizeof(*ev) + eir_len, NULL);
Johan Hedbergf7520542011-01-20 12:34:39 +02003121}
3122
Johan Hedberg8962ee72011-01-20 12:40:27 +02003123static void disconnect_rsp(struct pending_cmd *cmd, void *data)
3124{
Szymon Jancc68fb7f2011-03-22 13:12:19 +01003125 struct mgmt_cp_disconnect *cp = cmd->param;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003126 struct sock **sk = data;
Johan Hedberga38528f2011-01-22 06:46:43 +02003127 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003128
Johan Hedberg88c3df12012-02-09 14:27:38 +02003129 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
3130 rp.addr.type = cp->addr.type;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003131
Johan Hedbergaee9b212012-02-18 15:07:59 +02003132 cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
3133 sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02003134
3135 *sk = cmd->sk;
3136 sock_hold(*sk);
3137
Johan Hedberga664b5b2011-02-19 12:06:02 -03003138 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003139}
3140
Johan Hedberg124f6e32012-02-09 13:50:12 +02003141static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberga8a1d192011-11-10 15:54:38 +02003142{
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003143 struct hci_dev *hdev = data;
Johan Hedberg124f6e32012-02-09 13:50:12 +02003144 struct mgmt_cp_unpair_device *cp = cmd->param;
3145 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02003146
3147 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02003148 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
3149 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02003150
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003151 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
3152
Johan Hedbergaee9b212012-02-18 15:07:59 +02003153 cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02003154
3155 mgmt_pending_remove(cmd);
3156}
3157
Johan Hedbergafc747a2012-01-15 18:11:07 +02003158int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
3159 u8 link_type, u8 addr_type)
Johan Hedbergf7520542011-01-20 12:34:39 +02003160{
Johan Hedberg4c659c32011-11-07 23:13:39 +02003161 struct mgmt_addr_info ev;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003162 struct sock *sk = NULL;
3163 int err;
3164
Johan Hedberg744cf192011-11-08 20:40:14 +02003165 mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
Johan Hedbergf7520542011-01-20 12:34:39 +02003166
Johan Hedbergf7520542011-01-20 12:34:39 +02003167 bacpy(&ev.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02003168 ev.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02003169
Johan Hedbergafc747a2012-01-15 18:11:07 +02003170 err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
3171 sk);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003172
3173 if (sk)
3174 sock_put(sk);
3175
Johan Hedberg124f6e32012-02-09 13:50:12 +02003176 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003177 hdev);
Johan Hedberga8a1d192011-11-10 15:54:38 +02003178
Johan Hedberg8962ee72011-01-20 12:40:27 +02003179 return err;
3180}
3181
Johan Hedberg88c3df12012-02-09 14:27:38 +02003182int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
3183 u8 link_type, u8 addr_type, u8 status)
Johan Hedberg8962ee72011-01-20 12:40:27 +02003184{
Johan Hedberg88c3df12012-02-09 14:27:38 +02003185 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02003186 struct pending_cmd *cmd;
3187 int err;
3188
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003189 cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003190 if (!cmd)
3191 return -ENOENT;
3192
Johan Hedberg88c3df12012-02-09 14:27:38 +02003193 bacpy(&rp.addr.bdaddr, bdaddr);
3194 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg37d9ef72011-11-10 15:54:39 +02003195
Johan Hedberg88c3df12012-02-09 14:27:38 +02003196 err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
Johan Hedbergaee9b212012-02-18 15:07:59 +02003197 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02003198
Johan Hedberga664b5b2011-02-19 12:06:02 -03003199 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003200
Johan Hedbergb1078ad2012-02-09 17:21:16 +02003201 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
3202 hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02003203 return err;
Johan Hedbergf7520542011-01-20 12:34:39 +02003204}
Johan Hedberg17d5c042011-01-22 06:09:08 +02003205
Johan Hedberg48264f02011-11-09 13:58:58 +02003206int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3207 u8 addr_type, u8 status)
Johan Hedberg17d5c042011-01-22 06:09:08 +02003208{
3209 struct mgmt_ev_connect_failed ev;
3210
Johan Hedberg4c659c32011-11-07 23:13:39 +02003211 bacpy(&ev.addr.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02003212 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003213 ev.status = mgmt_status(status);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003214
Johan Hedberg744cf192011-11-08 20:40:14 +02003215 return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg17d5c042011-01-22 06:09:08 +02003216}
Johan Hedberg980e1a52011-01-22 06:10:07 +02003217
Johan Hedberg744cf192011-11-08 20:40:14 +02003218int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003219{
3220 struct mgmt_ev_pin_code_request ev;
3221
Johan Hedbergd8457692012-02-17 14:24:57 +02003222 bacpy(&ev.addr.bdaddr, bdaddr);
3223 ev.addr.type = MGMT_ADDR_BREDR;
Waldemar Rymarkiewicza770bb52011-04-28 12:07:59 +02003224 ev.secure = secure;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003225
Johan Hedberg744cf192011-11-08 20:40:14 +02003226 return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003227 NULL);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003228}
3229
Johan Hedberg744cf192011-11-08 20:40:14 +02003230int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3231 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003232{
3233 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003234 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003235 int err;
3236
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003237 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003238 if (!cmd)
3239 return -ENOENT;
3240
Johan Hedbergd8457692012-02-17 14:24:57 +02003241 bacpy(&rp.addr.bdaddr, bdaddr);
3242 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003243
Johan Hedbergaee9b212012-02-18 15:07:59 +02003244 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
3245 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003246
Johan Hedberga664b5b2011-02-19 12:06:02 -03003247 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003248
3249 return err;
3250}
3251
Johan Hedberg744cf192011-11-08 20:40:14 +02003252int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3253 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003254{
3255 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003256 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003257 int err;
3258
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003259 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003260 if (!cmd)
3261 return -ENOENT;
3262
Johan Hedbergd8457692012-02-17 14:24:57 +02003263 bacpy(&rp.addr.bdaddr, bdaddr);
3264 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003265
Johan Hedbergaee9b212012-02-18 15:07:59 +02003266 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
3267 mgmt_status(status), &rp, sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003268
Johan Hedberga664b5b2011-02-19 12:06:02 -03003269 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003270
3271 return err;
3272}
Johan Hedberga5c29682011-02-19 12:05:57 -03003273
Johan Hedberg744cf192011-11-08 20:40:14 +02003274int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003275 u8 link_type, u8 addr_type, __le32 value,
3276 u8 confirm_hint)
Johan Hedberga5c29682011-02-19 12:05:57 -03003277{
3278 struct mgmt_ev_user_confirm_request ev;
3279
Johan Hedberg744cf192011-11-08 20:40:14 +02003280 BT_DBG("%s", hdev->name);
Johan Hedberga5c29682011-02-19 12:05:57 -03003281
Johan Hedberg272d90d2012-02-09 15:26:12 +02003282 bacpy(&ev.addr.bdaddr, bdaddr);
3283 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg55bc1a32011-04-28 11:28:56 -07003284 ev.confirm_hint = confirm_hint;
Johan Hedberga5c29682011-02-19 12:05:57 -03003285 put_unaligned_le32(value, &ev.value);
3286
Johan Hedberg744cf192011-11-08 20:40:14 +02003287 return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003288 NULL);
Johan Hedberga5c29682011-02-19 12:05:57 -03003289}
3290
Johan Hedberg272d90d2012-02-09 15:26:12 +02003291int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
3292 u8 link_type, u8 addr_type)
Brian Gix604086b2011-11-23 08:28:33 -08003293{
3294 struct mgmt_ev_user_passkey_request ev;
3295
3296 BT_DBG("%s", hdev->name);
3297
Johan Hedberg272d90d2012-02-09 15:26:12 +02003298 bacpy(&ev.addr.bdaddr, bdaddr);
3299 ev.addr.type = link_to_mgmt(link_type, addr_type);
Brian Gix604086b2011-11-23 08:28:33 -08003300
3301 return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
3302 NULL);
3303}
3304
Brian Gix0df4c182011-11-16 13:53:13 -08003305static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003306 u8 link_type, u8 addr_type, u8 status,
3307 u8 opcode)
Johan Hedberga5c29682011-02-19 12:05:57 -03003308{
3309 struct pending_cmd *cmd;
3310 struct mgmt_rp_user_confirm_reply rp;
3311 int err;
3312
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003313 cmd = mgmt_pending_find(opcode, hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03003314 if (!cmd)
3315 return -ENOENT;
3316
Johan Hedberg272d90d2012-02-09 15:26:12 +02003317 bacpy(&rp.addr.bdaddr, bdaddr);
3318 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergaee9b212012-02-18 15:07:59 +02003319 err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
3320 &rp, sizeof(rp));
Johan Hedberga5c29682011-02-19 12:05:57 -03003321
Johan Hedberga664b5b2011-02-19 12:06:02 -03003322 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03003323
3324 return err;
3325}
3326
Johan Hedberg744cf192011-11-08 20:40:14 +02003327int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003328 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003329{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003330 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3331 status, MGMT_OP_USER_CONFIRM_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003332}
3333
Johan Hedberg272d90d2012-02-09 15:26:12 +02003334int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3335 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003336{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003337 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3338 status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003339}
Johan Hedberg2a611692011-02-19 12:06:00 -03003340
Brian Gix604086b2011-11-23 08:28:33 -08003341int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003342 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003343{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003344 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3345 status, MGMT_OP_USER_PASSKEY_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003346}
3347
Johan Hedberg272d90d2012-02-09 15:26:12 +02003348int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3349 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003350{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003351 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3352 status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003353}
3354
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003355int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3356 u8 addr_type, u8 status)
Johan Hedberg2a611692011-02-19 12:06:00 -03003357{
3358 struct mgmt_ev_auth_failed ev;
3359
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003360 bacpy(&ev.addr.bdaddr, bdaddr);
3361 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003362 ev.status = mgmt_status(status);
Johan Hedberg2a611692011-02-19 12:06:00 -03003363
Johan Hedberg744cf192011-11-08 20:40:14 +02003364 return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg2a611692011-02-19 12:06:00 -03003365}
Johan Hedbergb312b1612011-03-16 14:29:37 +02003366
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003367int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
3368{
3369 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg47990ea2012-02-22 11:58:37 +02003370 bool changed = false;
3371 int err = 0;
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003372
3373 if (status) {
3374 u8 mgmt_err = mgmt_status(status);
3375 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
3376 cmd_status_rsp, &mgmt_err);
3377 return 0;
3378 }
3379
Johan Hedberg47990ea2012-02-22 11:58:37 +02003380 if (test_bit(HCI_AUTH, &hdev->flags)) {
3381 if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
3382 changed = true;
3383 } else {
3384 if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags))
3385 changed = true;
3386 }
3387
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003388 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
3389 &match);
3390
Johan Hedberg47990ea2012-02-22 11:58:37 +02003391 if (changed)
3392 err = new_settings(hdev, match.sk);
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003393
3394 if (match.sk)
3395 sock_put(match.sk);
3396
3397 return err;
3398}
3399
Johan Hedbergcacaf522012-02-21 00:52:42 +02003400static int clear_eir(struct hci_dev *hdev)
3401{
3402 struct hci_cp_write_eir cp;
3403
3404 if (!(hdev->features[6] & LMP_EXT_INQ))
3405 return 0;
3406
Johan Hedbergc80da272012-02-22 15:38:48 +02003407 memset(hdev->eir, 0, sizeof(hdev->eir));
3408
Johan Hedbergcacaf522012-02-21 00:52:42 +02003409 memset(&cp, 0, sizeof(cp));
3410
3411 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
3412}
3413
Johan Hedbergc0ecddc2012-02-22 12:38:31 +02003414int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003415{
3416 struct cmd_lookup match = { NULL, hdev };
Johan Hedbergc0ecddc2012-02-22 12:38:31 +02003417 bool changed = false;
3418 int err = 0;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003419
3420 if (status) {
3421 u8 mgmt_err = mgmt_status(status);
Johan Hedbergc0ecddc2012-02-22 12:38:31 +02003422
3423 if (enable && test_and_clear_bit(HCI_SSP_ENABLED,
3424 &hdev->dev_flags))
3425 err = new_settings(hdev, NULL);
3426
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003427 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev,
3428 cmd_status_rsp, &mgmt_err);
Johan Hedbergc0ecddc2012-02-22 12:38:31 +02003429
3430 return err;
3431 }
3432
3433 if (enable) {
3434 if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
3435 changed = true;
3436 } else {
3437 if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
3438 changed = true;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003439 }
3440
3441 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
3442
Johan Hedbergc0ecddc2012-02-22 12:38:31 +02003443 if (changed)
3444 err = new_settings(hdev, match.sk);
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003445
Johan Hedberg5fc6ebb2012-02-22 15:10:59 +02003446 if (match.sk)
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003447 sock_put(match.sk);
3448
Johan Hedberg5fc6ebb2012-02-22 15:10:59 +02003449 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
3450 update_eir(hdev);
3451 else
3452 clear_eir(hdev);
Johan Hedbergcacaf522012-02-21 00:52:42 +02003453
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003454 return err;
3455}
3456
Johan Hedberg90e70452012-02-23 23:09:40 +02003457static void class_rsp(struct pending_cmd *cmd, void *data)
3458{
3459 struct cmd_lookup *match = data;
3460
3461 cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status,
3462 match->hdev->dev_class, 3);
3463
3464 list_del(&cmd->list);
3465
3466 if (match->sk == NULL) {
3467 match->sk = cmd->sk;
3468 sock_hold(match->sk);
3469 }
3470
3471 mgmt_pending_free(cmd);
3472}
3473
Marcel Holtmann7f9a9032012-02-22 18:38:01 +01003474int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class,
3475 u8 status)
3476{
Johan Hedberg90e70452012-02-23 23:09:40 +02003477 struct cmd_lookup match = { NULL, hdev, mgmt_status(status) };
3478 int err = 0;
Marcel Holtmann7f9a9032012-02-22 18:38:01 +01003479
Johan Hedbergc95f0ba2012-02-23 22:54:38 +02003480 clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags);
3481
Johan Hedberg90e70452012-02-23 23:09:40 +02003482 mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, class_rsp, &match);
3483 mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, class_rsp, &match);
3484 mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match);
3485
3486 if (!status)
3487 err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
3488 dev_class, 3, NULL);
3489
3490 if (match.sk)
3491 sock_put(match.sk);
Marcel Holtmann7f9a9032012-02-22 18:38:01 +01003492
3493 return err;
3494}
3495
Johan Hedberg744cf192011-11-08 20:40:14 +02003496int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
Johan Hedbergb312b1612011-03-16 14:29:37 +02003497{
3498 struct pending_cmd *cmd;
3499 struct mgmt_cp_set_local_name ev;
Johan Hedberg28cc7bd2012-02-22 21:06:55 +02003500 bool changed = false;
3501 int err = 0;
3502
3503 if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) {
3504 memcpy(hdev->dev_name, name, sizeof(hdev->dev_name));
3505 changed = true;
3506 }
Johan Hedbergb312b1612011-03-16 14:29:37 +02003507
3508 memset(&ev, 0, sizeof(ev));
3509 memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
Johan Hedberg28cc7bd2012-02-22 21:06:55 +02003510 memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH);
Johan Hedbergb312b1612011-03-16 14:29:37 +02003511
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003512 cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02003513 if (!cmd)
3514 goto send_event;
3515
Johan Hedberg7bdaae42012-02-22 21:39:58 +02003516 /* Always assume that either the short or the complete name has
3517 * changed if there was a pending mgmt command */
3518 changed = true;
3519
Johan Hedbergb312b1612011-03-16 14:29:37 +02003520 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003521 err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02003522 mgmt_status(status));
Johan Hedbergb312b1612011-03-16 14:29:37 +02003523 goto failed;
3524 }
3525
Johan Hedbergaee9b212012-02-18 15:07:59 +02003526 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev,
Johan Hedbergb312b1612011-03-16 14:29:37 +02003527 sizeof(ev));
3528 if (err < 0)
3529 goto failed;
3530
3531send_event:
Johan Hedberg28cc7bd2012-02-22 21:06:55 +02003532 if (changed)
3533 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev,
3534 sizeof(ev), cmd ? cmd->sk : NULL);
3535
Johan Hedbergf51d5b22012-02-22 18:17:32 +02003536 update_eir(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02003537
3538failed:
3539 if (cmd)
3540 mgmt_pending_remove(cmd);
3541 return err;
3542}
Szymon Jancc35938b2011-03-22 13:12:21 +01003543
Johan Hedberg744cf192011-11-08 20:40:14 +02003544int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
3545 u8 *randomizer, u8 status)
Szymon Jancc35938b2011-03-22 13:12:21 +01003546{
3547 struct pending_cmd *cmd;
3548 int err;
3549
Johan Hedberg744cf192011-11-08 20:40:14 +02003550 BT_DBG("%s status %u", hdev->name, status);
Szymon Jancc35938b2011-03-22 13:12:21 +01003551
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003552 cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01003553 if (!cmd)
3554 return -ENOENT;
3555
3556 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003557 err = cmd_status(cmd->sk, hdev->id,
Johan Hedbergca69b792011-11-11 18:10:00 +02003558 MGMT_OP_READ_LOCAL_OOB_DATA,
3559 mgmt_status(status));
Szymon Jancc35938b2011-03-22 13:12:21 +01003560 } else {
3561 struct mgmt_rp_read_local_oob_data rp;
3562
3563 memcpy(rp.hash, hash, sizeof(rp.hash));
3564 memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
3565
Johan Hedberg744cf192011-11-08 20:40:14 +02003566 err = cmd_complete(cmd->sk, hdev->id,
3567 MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergaee9b212012-02-18 15:07:59 +02003568 0, &rp, sizeof(rp));
Szymon Jancc35938b2011-03-22 13:12:21 +01003569 }
3570
3571 mgmt_pending_remove(cmd);
3572
3573 return err;
3574}
Johan Hedberge17acd42011-03-30 23:57:16 +03003575
Johan Hedberg06199cf2012-02-22 16:37:11 +02003576int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status)
3577{
3578 struct cmd_lookup match = { NULL, hdev };
3579 bool changed = false;
3580 int err = 0;
3581
3582 if (status) {
3583 u8 mgmt_err = mgmt_status(status);
3584
3585 if (enable && test_and_clear_bit(HCI_LE_ENABLED,
3586 &hdev->dev_flags))
3587 err = new_settings(hdev, NULL);
3588
3589 mgmt_pending_foreach(MGMT_OP_SET_LE, hdev,
3590 cmd_status_rsp, &mgmt_err);
3591
3592 return err;
3593 }
3594
3595 if (enable) {
3596 if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags))
3597 changed = true;
3598 } else {
3599 if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags))
3600 changed = true;
3601 }
3602
3603 mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match);
3604
3605 if (changed)
3606 err = new_settings(hdev, match.sk);
3607
3608 if (match.sk)
3609 sock_put(match.sk);
3610
3611 return err;
3612}
3613
Johan Hedberg48264f02011-11-09 13:58:58 +02003614int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg561aafb2012-01-04 13:31:59 +02003615 u8 addr_type, u8 *dev_class, s8 rssi,
Johan Hedberg388fc8f2012-02-23 00:38:59 +02003616 u8 cfm_name, u8 ssp, u8 *eir, u16 eir_len)
Johan Hedberge17acd42011-03-30 23:57:16 +03003617{
Johan Hedberge319d2e2012-01-15 19:51:59 +02003618 char buf[512];
3619 struct mgmt_ev_device_found *ev = (void *) buf;
Johan Hedberg1dc06092012-01-15 21:01:23 +02003620 size_t ev_size;
Johan Hedberge17acd42011-03-30 23:57:16 +03003621
Johan Hedberg1dc06092012-01-15 21:01:23 +02003622 /* Leave 5 bytes for a potential CoD field */
3623 if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
Andre Guedes7d262f82012-01-10 18:20:49 -03003624 return -EINVAL;
3625
Johan Hedberg1dc06092012-01-15 21:01:23 +02003626 memset(buf, 0, sizeof(buf));
3627
Johan Hedberge319d2e2012-01-15 19:51:59 +02003628 bacpy(&ev->addr.bdaddr, bdaddr);
3629 ev->addr.type = link_to_mgmt(link_type, addr_type);
3630 ev->rssi = rssi;
Johan Hedberg9a395a82012-02-23 00:00:32 +02003631 if (cfm_name)
3632 ev->flags[0] |= MGMT_DEV_FOUND_CONFIRM_NAME;
Johan Hedberg388fc8f2012-02-23 00:38:59 +02003633 if (!ssp)
3634 ev->flags[0] |= MGMT_DEV_FOUND_LEGACY_PAIRING;
Johan Hedberge17acd42011-03-30 23:57:16 +03003635
Johan Hedberg1dc06092012-01-15 21:01:23 +02003636 if (eir_len > 0)
Johan Hedberge319d2e2012-01-15 19:51:59 +02003637 memcpy(ev->eir, eir, eir_len);
Johan Hedberge17acd42011-03-30 23:57:16 +03003638
Johan Hedberg1dc06092012-01-15 21:01:23 +02003639 if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
3640 eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
3641 dev_class, 3);
3642
3643 put_unaligned_le16(eir_len, &ev->eir_len);
3644
3645 ev_size = sizeof(*ev) + eir_len;
Andre Guedesf8523592011-09-09 18:56:26 -03003646
Johan Hedberge319d2e2012-01-15 19:51:59 +02003647 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
Johan Hedberge17acd42011-03-30 23:57:16 +03003648}
Johan Hedberga88a9652011-03-30 13:18:12 +03003649
Johan Hedbergb644ba32012-01-17 21:48:47 +02003650int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3651 u8 addr_type, s8 rssi, u8 *name, u8 name_len)
Johan Hedberga88a9652011-03-30 13:18:12 +03003652{
Johan Hedbergb644ba32012-01-17 21:48:47 +02003653 struct mgmt_ev_device_found *ev;
3654 char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
3655 u16 eir_len;
Johan Hedberga88a9652011-03-30 13:18:12 +03003656
Johan Hedbergb644ba32012-01-17 21:48:47 +02003657 ev = (struct mgmt_ev_device_found *) buf;
Johan Hedberga88a9652011-03-30 13:18:12 +03003658
Johan Hedbergb644ba32012-01-17 21:48:47 +02003659 memset(buf, 0, sizeof(buf));
Johan Hedberga88a9652011-03-30 13:18:12 +03003660
Johan Hedbergb644ba32012-01-17 21:48:47 +02003661 bacpy(&ev->addr.bdaddr, bdaddr);
3662 ev->addr.type = link_to_mgmt(link_type, addr_type);
3663 ev->rssi = rssi;
3664
3665 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
3666 name_len);
3667
3668 put_unaligned_le16(eir_len, &ev->eir_len);
3669
Johan Hedberg053c7e02012-02-04 00:06:00 +02003670 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
3671 sizeof(*ev) + eir_len, NULL);
Johan Hedberga88a9652011-03-30 13:18:12 +03003672}
Johan Hedberg314b2382011-04-27 10:29:57 -04003673
Andre Guedes7a135102011-11-09 17:14:25 -03003674int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
Johan Hedberg164a6e72011-11-01 17:06:44 +02003675{
3676 struct pending_cmd *cmd;
Johan Hedbergf808e162012-02-19 12:52:07 +02003677 u8 type;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003678 int err;
3679
Andre Guedes203159d2012-02-13 15:41:01 -03003680 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
3681
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003682 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003683 if (!cmd)
3684 return -ENOENT;
3685
Johan Hedbergf808e162012-02-19 12:52:07 +02003686 type = hdev->discovery.type;
3687
3688 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3689 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003690 mgmt_pending_remove(cmd);
3691
3692 return err;
3693}
3694
Andre Guedese6d465c2011-11-09 17:14:26 -03003695int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
3696{
3697 struct pending_cmd *cmd;
3698 int err;
3699
3700 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
3701 if (!cmd)
3702 return -ENOENT;
3703
Johan Hedbergd9306502012-02-20 23:25:18 +02003704 err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
3705 &hdev->discovery.type,
3706 sizeof(hdev->discovery.type));
Johan Hedberg03811012010-12-08 00:21:06 +02003707 mgmt_pending_remove(cmd);
3708
3709 return err;
3710}
Johan Hedberg314b2382011-04-27 10:29:57 -04003711
Johan Hedberg744cf192011-11-08 20:40:14 +02003712int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
Johan Hedberg314b2382011-04-27 10:29:57 -04003713{
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003714 struct mgmt_ev_discovering ev;
Johan Hedberg164a6e72011-11-01 17:06:44 +02003715 struct pending_cmd *cmd;
3716
Andre Guedes343fb142011-11-22 17:14:19 -03003717 BT_DBG("%s discovering %u", hdev->name, discovering);
3718
Johan Hedberg164a6e72011-11-01 17:06:44 +02003719 if (discovering)
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003720 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003721 else
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003722 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003723
3724 if (cmd != NULL) {
Johan Hedbergf808e162012-02-19 12:52:07 +02003725 u8 type = hdev->discovery.type;
3726
Johan Hedbergd9306502012-02-20 23:25:18 +02003727 cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0,
Johan Hedbergf808e162012-02-19 12:52:07 +02003728 &type, sizeof(type));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003729 mgmt_pending_remove(cmd);
3730 }
3731
Johan Hedbergf963e8e2012-02-20 23:30:44 +02003732 memset(&ev, 0, sizeof(ev));
3733 ev.type = hdev->discovery.type;
3734 ev.discovering = discovering;
3735
3736 return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg314b2382011-04-27 10:29:57 -04003737}
Antti Julku5e762442011-08-25 16:48:02 +03003738
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003739int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003740{
3741 struct pending_cmd *cmd;
3742 struct mgmt_ev_device_blocked ev;
3743
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003744 cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003745
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003746 bacpy(&ev.addr.bdaddr, bdaddr);
3747 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003748
Johan Hedberg744cf192011-11-08 20:40:14 +02003749 return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
3750 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003751}
3752
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003753int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003754{
3755 struct pending_cmd *cmd;
3756 struct mgmt_ev_device_unblocked ev;
3757
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003758 cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003759
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003760 bacpy(&ev.addr.bdaddr, bdaddr);
3761 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003762
Johan Hedberg744cf192011-11-08 20:40:14 +02003763 return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
3764 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003765}
Marcel Holtmannd7b7e792012-02-20 21:47:49 +01003766
3767module_param(enable_hs, bool, 0644);
3768MODULE_PARM_DESC(enable_hs, "Enable High Speed support");
3769
3770module_param(enable_le, bool, 0644);
3771MODULE_PARM_DESC(enable_le, "Enable Low Energy support");