blob: 89754bbcd02bf45f8c1a8883341fdc60e9aab3cf [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
Johan Hedberg02d98122010-12-13 21:07:04 +020037#define MGMT_VERSION 0
38#define MGMT_REVISION 1
39
Johan Hedberge70bb2e2012-02-13 16:59:33 +020040static const u16 mgmt_commands[] = {
41 MGMT_OP_READ_INDEX_LIST,
42 MGMT_OP_READ_INFO,
43 MGMT_OP_SET_POWERED,
44 MGMT_OP_SET_DISCOVERABLE,
45 MGMT_OP_SET_CONNECTABLE,
46 MGMT_OP_SET_FAST_CONNECTABLE,
47 MGMT_OP_SET_PAIRABLE,
48 MGMT_OP_SET_LINK_SECURITY,
49 MGMT_OP_SET_SSP,
50 MGMT_OP_SET_HS,
51 MGMT_OP_SET_LE,
52 MGMT_OP_SET_DEV_CLASS,
53 MGMT_OP_SET_LOCAL_NAME,
54 MGMT_OP_ADD_UUID,
55 MGMT_OP_REMOVE_UUID,
56 MGMT_OP_LOAD_LINK_KEYS,
57 MGMT_OP_LOAD_LONG_TERM_KEYS,
58 MGMT_OP_DISCONNECT,
59 MGMT_OP_GET_CONNECTIONS,
60 MGMT_OP_PIN_CODE_REPLY,
61 MGMT_OP_PIN_CODE_NEG_REPLY,
62 MGMT_OP_SET_IO_CAPABILITY,
63 MGMT_OP_PAIR_DEVICE,
64 MGMT_OP_CANCEL_PAIR_DEVICE,
65 MGMT_OP_UNPAIR_DEVICE,
66 MGMT_OP_USER_CONFIRM_REPLY,
67 MGMT_OP_USER_CONFIRM_NEG_REPLY,
68 MGMT_OP_USER_PASSKEY_REPLY,
69 MGMT_OP_USER_PASSKEY_NEG_REPLY,
70 MGMT_OP_READ_LOCAL_OOB_DATA,
71 MGMT_OP_ADD_REMOTE_OOB_DATA,
72 MGMT_OP_REMOVE_REMOTE_OOB_DATA,
73 MGMT_OP_START_DISCOVERY,
74 MGMT_OP_STOP_DISCOVERY,
75 MGMT_OP_CONFIRM_NAME,
76 MGMT_OP_BLOCK_DEVICE,
77 MGMT_OP_UNBLOCK_DEVICE,
78};
79
80static const u16 mgmt_events[] = {
81 MGMT_EV_CONTROLLER_ERROR,
82 MGMT_EV_INDEX_ADDED,
83 MGMT_EV_INDEX_REMOVED,
84 MGMT_EV_NEW_SETTINGS,
85 MGMT_EV_CLASS_OF_DEV_CHANGED,
86 MGMT_EV_LOCAL_NAME_CHANGED,
87 MGMT_EV_NEW_LINK_KEY,
88 MGMT_EV_NEW_LONG_TERM_KEY,
89 MGMT_EV_DEVICE_CONNECTED,
90 MGMT_EV_DEVICE_DISCONNECTED,
91 MGMT_EV_CONNECT_FAILED,
92 MGMT_EV_PIN_CODE_REQUEST,
93 MGMT_EV_USER_CONFIRM_REQUEST,
94 MGMT_EV_USER_PASSKEY_REQUEST,
95 MGMT_EV_AUTH_FAILED,
96 MGMT_EV_DEVICE_FOUND,
97 MGMT_EV_DISCOVERING,
98 MGMT_EV_DEVICE_BLOCKED,
99 MGMT_EV_DEVICE_UNBLOCKED,
100 MGMT_EV_DEVICE_UNPAIRED,
101};
102
Andre Guedes3fd24152012-02-03 17:48:01 -0300103/*
104 * These LE scan and inquiry parameters were chosen according to LE General
105 * Discovery Procedure specification.
106 */
107#define LE_SCAN_TYPE 0x01
108#define LE_SCAN_WIN 0x12
109#define LE_SCAN_INT 0x12
110#define LE_SCAN_TIMEOUT_LE_ONLY 10240 /* TGAP(gen_disc_scan_min) */
Andre Guedes5e0452c2012-02-17 20:39:38 -0300111#define LE_SCAN_TIMEOUT_BREDR_LE 5120 /* TGAP(100)/2 */
Andre Guedes3fd24152012-02-03 17:48:01 -0300112
Andre Guedese8777522012-02-03 17:48:02 -0300113#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */
Andre Guedes5e0452c2012-02-17 20:39:38 -0300114#define INQUIRY_LEN_BREDR_LE 0x04 /* TGAP(100)/2 */
Andre Guedes2519a1f2011-11-07 11:45:24 -0300115
Johan Hedberg7d785252011-12-15 00:47:39 +0200116#define SERVICE_CACHE_TIMEOUT (5 * 1000)
117
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200118struct pending_cmd {
119 struct list_head list;
Johan Hedbergfc2f4b12011-11-09 13:58:56 +0200120 u16 opcode;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200121 int index;
Szymon Jancc68fb7f2011-03-22 13:12:19 +0100122 void *param;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200123 struct sock *sk;
Johan Hedberge9a416b2011-02-19 12:05:56 -0300124 void *user_data;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200125};
126
Johan Hedbergca69b792011-11-11 18:10:00 +0200127/* HCI to MGMT error code conversion table */
128static u8 mgmt_status_table[] = {
129 MGMT_STATUS_SUCCESS,
130 MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */
131 MGMT_STATUS_NOT_CONNECTED, /* No Connection */
132 MGMT_STATUS_FAILED, /* Hardware Failure */
133 MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */
134 MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */
135 MGMT_STATUS_NOT_PAIRED, /* PIN or Key Missing */
136 MGMT_STATUS_NO_RESOURCES, /* Memory Full */
137 MGMT_STATUS_TIMEOUT, /* Connection Timeout */
138 MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */
139 MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */
140 MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */
141 MGMT_STATUS_BUSY, /* Command Disallowed */
142 MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */
143 MGMT_STATUS_REJECTED, /* Rejected Security */
144 MGMT_STATUS_REJECTED, /* Rejected Personal */
145 MGMT_STATUS_TIMEOUT, /* Host Timeout */
146 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */
147 MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */
148 MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */
149 MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */
150 MGMT_STATUS_DISCONNECTED, /* OE Power Off */
151 MGMT_STATUS_DISCONNECTED, /* Connection Terminated */
152 MGMT_STATUS_BUSY, /* Repeated Attempts */
153 MGMT_STATUS_REJECTED, /* Pairing Not Allowed */
154 MGMT_STATUS_FAILED, /* Unknown LMP PDU */
155 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */
156 MGMT_STATUS_REJECTED, /* SCO Offset Rejected */
157 MGMT_STATUS_REJECTED, /* SCO Interval Rejected */
158 MGMT_STATUS_REJECTED, /* Air Mode Rejected */
159 MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */
160 MGMT_STATUS_FAILED, /* Unspecified Error */
161 MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */
162 MGMT_STATUS_FAILED, /* Role Change Not Allowed */
163 MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */
164 MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */
165 MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */
166 MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */
167 MGMT_STATUS_FAILED, /* Unit Link Key Used */
168 MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */
169 MGMT_STATUS_TIMEOUT, /* Instant Passed */
170 MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */
171 MGMT_STATUS_FAILED, /* Transaction Collision */
172 MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */
173 MGMT_STATUS_REJECTED, /* QoS Rejected */
174 MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */
175 MGMT_STATUS_REJECTED, /* Insufficient Security */
176 MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */
177 MGMT_STATUS_BUSY, /* Role Switch Pending */
178 MGMT_STATUS_FAILED, /* Slot Violation */
179 MGMT_STATUS_FAILED, /* Role Switch Failed */
180 MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */
181 MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */
182 MGMT_STATUS_BUSY, /* Host Busy Pairing */
183 MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */
184 MGMT_STATUS_BUSY, /* Controller Busy */
185 MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */
186 MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */
187 MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */
188 MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */
189 MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */
190};
191
192static u8 mgmt_status(u8 hci_status)
193{
194 if (hci_status < ARRAY_SIZE(mgmt_status_table))
195 return mgmt_status_table[hci_status];
196
197 return MGMT_STATUS_FAILED;
198}
199
Szymon Janc4e51eae2011-02-25 19:05:48 +0100200static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200201{
202 struct sk_buff *skb;
203 struct mgmt_hdr *hdr;
204 struct mgmt_ev_cmd_status *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300205 int err;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200206
Szymon Janc34eb5252011-02-28 14:10:08 +0100207 BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200208
209 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
210 if (!skb)
211 return -ENOMEM;
212
213 hdr = (void *) skb_put(skb, sizeof(*hdr));
214
215 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100216 hdr->index = cpu_to_le16(index);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200217 hdr->len = cpu_to_le16(sizeof(*ev));
218
219 ev = (void *) skb_put(skb, sizeof(*ev));
220 ev->status = status;
221 put_unaligned_le16(cmd, &ev->opcode);
222
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300223 err = sock_queue_rcv_skb(sk, skb);
224 if (err < 0)
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200225 kfree_skb(skb);
226
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300227 return err;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200228}
229
Szymon Janc4e51eae2011-02-25 19:05:48 +0100230static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp,
231 size_t rp_len)
Johan Hedberg02d98122010-12-13 21:07:04 +0200232{
233 struct sk_buff *skb;
234 struct mgmt_hdr *hdr;
235 struct mgmt_ev_cmd_complete *ev;
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300236 int err;
Johan Hedberg02d98122010-12-13 21:07:04 +0200237
238 BT_DBG("sock %p", sk);
239
Johan Hedberga38528f2011-01-22 06:46:43 +0200240 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC);
Johan Hedberg02d98122010-12-13 21:07:04 +0200241 if (!skb)
242 return -ENOMEM;
243
244 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedberga38528f2011-01-22 06:46:43 +0200245
Johan Hedberg02d98122010-12-13 21:07:04 +0200246 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
Szymon Janc4e51eae2011-02-25 19:05:48 +0100247 hdr->index = cpu_to_le16(index);
Johan Hedberga38528f2011-01-22 06:46:43 +0200248 hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200249
Johan Hedberga38528f2011-01-22 06:46:43 +0200250 ev = (void *) skb_put(skb, sizeof(*ev) + rp_len);
251 put_unaligned_le16(cmd, &ev->opcode);
Szymon Janc8020c162011-02-28 14:09:50 +0100252
253 if (rp)
254 memcpy(ev->data, rp, rp_len);
Johan Hedberg02d98122010-12-13 21:07:04 +0200255
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300256 err = sock_queue_rcv_skb(sk, skb);
257 if (err < 0)
Johan Hedberg02d98122010-12-13 21:07:04 +0200258 kfree_skb(skb);
259
Gustavo F. Padovan56b7d132011-10-14 19:20:01 -0300260 return err;;
Johan Hedberg02d98122010-12-13 21:07:04 +0200261}
262
Johan Hedberga38528f2011-01-22 06:46:43 +0200263static int read_version(struct sock *sk)
264{
265 struct mgmt_rp_read_version rp;
266
267 BT_DBG("sock %p", sk);
268
269 rp.version = MGMT_VERSION;
270 put_unaligned_le16(MGMT_REVISION, &rp.revision);
271
Szymon Janc4e51eae2011-02-25 19:05:48 +0100272 return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, &rp,
273 sizeof(rp));
Johan Hedberga38528f2011-01-22 06:46:43 +0200274}
275
Johan Hedberge70bb2e2012-02-13 16:59:33 +0200276static int read_commands(struct sock *sk)
277{
278 struct mgmt_rp_read_commands *rp;
279 u16 num_commands = ARRAY_SIZE(mgmt_commands);
280 u16 num_events = ARRAY_SIZE(mgmt_events);
281 u16 *opcode;
282 size_t rp_size;
283 int i, err;
284
285 BT_DBG("sock %p", sk);
286
287 rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16));
288
289 rp = kmalloc(rp_size, GFP_KERNEL);
290 if (!rp)
291 return -ENOMEM;
292
293 put_unaligned_le16(num_commands, &rp->num_commands);
294 put_unaligned_le16(num_events, &rp->num_events);
295
296 for (i = 0, opcode = rp->opcodes; i < num_commands; i++, opcode++)
297 put_unaligned_le16(mgmt_commands[i], opcode);
298
299 for (i = 0; i < num_events; i++, opcode++)
300 put_unaligned_le16(mgmt_events[i], opcode);
301
302 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, rp,
303 rp_size);
304 kfree(rp);
305
306 return err;
307}
308
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200309static int read_index_list(struct sock *sk)
310{
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200311 struct mgmt_rp_read_index_list *rp;
312 struct list_head *p;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200313 struct hci_dev *d;
Johan Hedberga38528f2011-01-22 06:46:43 +0200314 size_t rp_len;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200315 u16 count;
Johan Hedberga38528f2011-01-22 06:46:43 +0200316 int i, err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200317
318 BT_DBG("sock %p", sk);
319
320 read_lock(&hci_dev_list_lock);
321
322 count = 0;
323 list_for_each(p, &hci_dev_list) {
324 count++;
325 }
326
Johan Hedberga38528f2011-01-22 06:46:43 +0200327 rp_len = sizeof(*rp) + (2 * count);
328 rp = kmalloc(rp_len, GFP_ATOMIC);
329 if (!rp) {
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100330 read_unlock(&hci_dev_list_lock);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200331 return -ENOMEM;
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100332 }
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200333
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200334 put_unaligned_le16(count, &rp->num_controllers);
335
336 i = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200337 list_for_each_entry(d, &hci_dev_list, list) {
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200338 if (test_and_clear_bit(HCI_AUTO_OFF, &d->dev_flags))
Johan Hedberge0f93092011-11-09 01:44:22 +0200339 cancel_delayed_work(&d->power_off);
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200340
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200341 if (test_bit(HCI_SETUP, &d->dev_flags))
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200342 continue;
343
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200344 put_unaligned_le16(d->id, &rp->index[i++]);
345 BT_DBG("Added hci%u", d->id);
346 }
347
348 read_unlock(&hci_dev_list_lock);
349
Szymon Janc4e51eae2011-02-25 19:05:48 +0100350 err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, rp,
351 rp_len);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200352
Johan Hedberga38528f2011-01-22 06:46:43 +0200353 kfree(rp);
354
355 return err;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200356}
357
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200358static u32 get_supported_settings(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +0200359{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200360 u32 settings = 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200361
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200362 settings |= MGMT_SETTING_POWERED;
363 settings |= MGMT_SETTING_CONNECTABLE;
364 settings |= MGMT_SETTING_FAST_CONNECTABLE;
365 settings |= MGMT_SETTING_DISCOVERABLE;
366 settings |= MGMT_SETTING_PAIRABLE;
Johan Hedberg03811012010-12-08 00:21:06 +0200367
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200368 if (hdev->features[6] & LMP_SIMPLE_PAIR)
369 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200370
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200371 if (!(hdev->features[4] & LMP_NO_BREDR)) {
372 settings |= MGMT_SETTING_BREDR;
373 settings |= MGMT_SETTING_LINK_SECURITY;
374 }
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200375
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200376 if (hdev->features[4] & LMP_LE)
377 settings |= MGMT_SETTING_LE;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200378
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200379 return settings;
380}
Johan Hedbergebc99fe2011-01-04 11:54:26 +0200381
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200382static u32 get_current_settings(struct hci_dev *hdev)
383{
384 u32 settings = 0;
Johan Hedbergdc4fe302011-03-16 14:29:36 +0200385
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200386 if (test_bit(HCI_UP, &hdev->flags))
387 settings |= MGMT_SETTING_POWERED;
388 else
389 return settings;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200390
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200391 if (test_bit(HCI_PSCAN, &hdev->flags))
392 settings |= MGMT_SETTING_CONNECTABLE;
393
394 if (test_bit(HCI_ISCAN, &hdev->flags))
395 settings |= MGMT_SETTING_DISCOVERABLE;
396
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200397 if (test_bit(HCI_PAIRABLE, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200398 settings |= MGMT_SETTING_PAIRABLE;
399
400 if (!(hdev->features[4] & LMP_NO_BREDR))
401 settings |= MGMT_SETTING_BREDR;
402
Andre Guedes59e29402011-12-30 10:34:03 -0300403 if (hdev->host_features[0] & LMP_HOST_LE)
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200404 settings |= MGMT_SETTING_LE;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200405
406 if (test_bit(HCI_AUTH, &hdev->flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200407 settings |= MGMT_SETTING_LINK_SECURITY;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200408
Johan Hedberg84bde9d2012-01-25 14:21:06 +0200409 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200410 settings |= MGMT_SETTING_SSP;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200411
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200412 return settings;
Johan Hedbergc542a062011-01-26 13:11:03 +0200413}
414
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300415#define PNP_INFO_SVCLASS_ID 0x1200
416
417static u8 bluetooth_base_uuid[] = {
418 0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
419 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
420};
421
422static u16 get_uuid16(u8 *uuid128)
423{
424 u32 val;
425 int i;
426
427 for (i = 0; i < 12; i++) {
428 if (bluetooth_base_uuid[i] != uuid128[i])
429 return 0;
430 }
431
432 memcpy(&val, &uuid128[12], 4);
433
434 val = le32_to_cpu(val);
435 if (val > 0xffff)
436 return 0;
437
438 return (u16) val;
439}
440
441static void create_eir(struct hci_dev *hdev, u8 *data)
442{
443 u8 *ptr = data;
444 u16 eir_len = 0;
445 u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)];
446 int i, truncated = 0;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200447 struct bt_uuid *uuid;
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300448 size_t name_len;
449
450 name_len = strlen(hdev->dev_name);
451
452 if (name_len > 0) {
453 /* EIR Data type */
454 if (name_len > 48) {
455 name_len = 48;
456 ptr[1] = EIR_NAME_SHORT;
457 } else
458 ptr[1] = EIR_NAME_COMPLETE;
459
460 /* EIR Data length */
461 ptr[0] = name_len + 1;
462
463 memcpy(ptr + 2, hdev->dev_name, name_len);
464
465 eir_len += (name_len + 2);
466 ptr += (name_len + 2);
467 }
468
469 memset(uuid16_list, 0, sizeof(uuid16_list));
470
471 /* Group all UUID16 types */
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200472 list_for_each_entry(uuid, &hdev->uuids, list) {
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300473 u16 uuid16;
474
475 uuid16 = get_uuid16(uuid->uuid);
476 if (uuid16 == 0)
477 return;
478
479 if (uuid16 < 0x1100)
480 continue;
481
482 if (uuid16 == PNP_INFO_SVCLASS_ID)
483 continue;
484
485 /* Stop if not enough space to put next UUID */
486 if (eir_len + 2 + sizeof(u16) > HCI_MAX_EIR_LENGTH) {
487 truncated = 1;
488 break;
489 }
490
491 /* Check for duplicates */
492 for (i = 0; uuid16_list[i] != 0; i++)
493 if (uuid16_list[i] == uuid16)
494 break;
495
496 if (uuid16_list[i] == 0) {
497 uuid16_list[i] = uuid16;
498 eir_len += sizeof(u16);
499 }
500 }
501
502 if (uuid16_list[0] != 0) {
503 u8 *length = ptr;
504
505 /* EIR Data type */
506 ptr[1] = truncated ? EIR_UUID16_SOME : EIR_UUID16_ALL;
507
508 ptr += 2;
509 eir_len += 2;
510
511 for (i = 0; uuid16_list[i] != 0; i++) {
512 *ptr++ = (uuid16_list[i] & 0x00ff);
513 *ptr++ = (uuid16_list[i] & 0xff00) >> 8;
514 }
515
516 /* EIR Data length */
517 *length = (i * sizeof(u16)) + 1;
518 }
519}
520
521static int update_eir(struct hci_dev *hdev)
522{
523 struct hci_cp_write_eir cp;
524
525 if (!(hdev->features[6] & LMP_EXT_INQ))
526 return 0;
527
Johan Hedberg84bde9d2012-01-25 14:21:06 +0200528 if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300529 return 0;
530
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200531 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg80a1e1d2011-03-28 14:07:23 +0300532 return 0;
533
534 memset(&cp, 0, sizeof(cp));
535
536 create_eir(hdev, cp.data);
537
538 if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0)
539 return 0;
540
541 memcpy(hdev->eir, cp.data, sizeof(cp.data));
542
543 return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
544}
545
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200546static u8 get_service_classes(struct hci_dev *hdev)
547{
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300548 struct bt_uuid *uuid;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200549 u8 val = 0;
550
Gustavo F. Padovan12dc0742011-10-14 19:32:56 -0300551 list_for_each_entry(uuid, &hdev->uuids, list)
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200552 val |= uuid->svc_hint;
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200553
554 return val;
555}
556
557static int update_class(struct hci_dev *hdev)
558{
559 u8 cod[3];
560
561 BT_DBG("%s", hdev->name);
562
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200563 if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg1aff6f02011-01-13 21:56:52 +0200564 return 0;
565
566 cod[0] = hdev->minor_class;
567 cod[1] = hdev->major_class;
568 cod[2] = get_service_classes(hdev);
569
570 if (memcmp(cod, hdev->dev_class, 3) == 0)
571 return 0;
572
573 return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
574}
575
Johan Hedberg7d785252011-12-15 00:47:39 +0200576static void service_cache_off(struct work_struct *work)
577{
578 struct hci_dev *hdev = container_of(work, struct hci_dev,
579 service_cache.work);
580
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200581 if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200582 return;
583
584 hci_dev_lock(hdev);
585
586 update_eir(hdev);
587 update_class(hdev);
588
589 hci_dev_unlock(hdev);
590}
591
592static void mgmt_init_hdev(struct hci_dev *hdev)
593{
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200594 if (!test_and_set_bit(HCI_MGMT, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200595 INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off);
596
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200597 if (!test_and_set_bit(HCI_SERVICE_CACHE, &hdev->dev_flags))
Johan Hedberg7d785252011-12-15 00:47:39 +0200598 schedule_delayed_work(&hdev->service_cache,
599 msecs_to_jiffies(SERVICE_CACHE_TIMEOUT));
600}
601
Johan Hedberg03811012010-12-08 00:21:06 +0200602static int read_controller_info(struct sock *sk, u16 index)
603{
604 struct mgmt_rp_read_info rp;
605 struct hci_dev *hdev;
606
607 BT_DBG("sock %p hci%u", sk, index);
608
609 hdev = hci_dev_get(index);
610 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200611 return cmd_status(sk, index, MGMT_OP_READ_INFO,
612 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200613
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200614 if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags))
Johan Hedberg03811012010-12-08 00:21:06 +0200615 cancel_delayed_work_sync(&hdev->power_off);
616
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300617 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200618
Johan Hedberg7d785252011-12-15 00:47:39 +0200619 if (test_and_clear_bit(HCI_PI_MGMT_INIT, &hci_pi(sk)->flags))
620 mgmt_init_hdev(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200621
622 memset(&rp, 0, sizeof(rp));
623
Johan Hedberg03811012010-12-08 00:21:06 +0200624 bacpy(&rp.bdaddr, &hdev->bdaddr);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200625
626 rp.version = hdev->hci_ver;
627
Johan Hedberg03811012010-12-08 00:21:06 +0200628 put_unaligned_le16(hdev->manufacturer, &rp.manufacturer);
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200629
630 rp.supported_settings = cpu_to_le32(get_supported_settings(hdev));
631 rp.current_settings = cpu_to_le32(get_current_settings(hdev));
632
633 memcpy(rp.dev_class, hdev->dev_class, 3);
Johan Hedberg03811012010-12-08 00:21:06 +0200634
635 memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
636
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300637 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200638 hci_dev_put(hdev);
639
640 return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
641}
642
643static void mgmt_pending_free(struct pending_cmd *cmd)
644{
645 sock_put(cmd->sk);
646 kfree(cmd->param);
647 kfree(cmd);
648}
649
650static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
651 struct hci_dev *hdev,
652 void *data, u16 len)
653{
654 struct pending_cmd *cmd;
655
656 cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC);
657 if (!cmd)
658 return NULL;
659
660 cmd->opcode = opcode;
661 cmd->index = hdev->id;
662
663 cmd->param = kmalloc(len, GFP_ATOMIC);
664 if (!cmd->param) {
665 kfree(cmd);
666 return NULL;
667 }
668
669 if (data)
670 memcpy(cmd->param, data, len);
671
672 cmd->sk = sk;
673 sock_hold(sk);
674
675 list_add(&cmd->list, &hdev->mgmt_pending);
676
677 return cmd;
678}
679
680static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
681 void (*cb)(struct pending_cmd *cmd, void *data),
682 void *data)
683{
684 struct list_head *p, *n;
685
686 list_for_each_safe(p, n, &hdev->mgmt_pending) {
687 struct pending_cmd *cmd;
688
689 cmd = list_entry(p, struct pending_cmd, list);
690
691 if (opcode > 0 && cmd->opcode != opcode)
692 continue;
693
694 cb(cmd, data);
695 }
696}
697
698static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev)
699{
700 struct pending_cmd *cmd;
701
702 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
703 if (cmd->opcode == opcode)
704 return cmd;
705 }
706
707 return NULL;
708}
709
710static void mgmt_pending_remove(struct pending_cmd *cmd)
711{
712 list_del(&cmd->list);
713 mgmt_pending_free(cmd);
714}
715
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200716static int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev)
Johan Hedberg86805702011-11-11 16:18:52 +0200717{
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200718 __le32 settings = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg86805702011-11-11 16:18:52 +0200719
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200720 return cmd_complete(sk, hdev->id, opcode, &settings, sizeof(settings));
Johan Hedberg86805702011-11-11 16:18:52 +0200721}
722
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300723static int set_powered(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200724{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300725 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200726 struct hci_dev *hdev;
727 struct pending_cmd *cmd;
728 int err, up;
729
Johan Hedberg03811012010-12-08 00:21:06 +0200730 BT_DBG("request for hci%u", index);
731
732 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200733 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
734 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200735
736 hdev = hci_dev_get(index);
737 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200738 return cmd_status(sk, index, MGMT_OP_SET_POWERED,
739 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200740
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300741 hci_dev_lock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200742
743 up = test_bit(HCI_UP, &hdev->flags);
744 if ((cp->val && up) || (!cp->val && !up)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200745 err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200746 goto failed;
747 }
748
749 if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200750 err = cmd_status(sk, index, MGMT_OP_SET_POWERED,
751 MGMT_STATUS_BUSY);
Johan Hedberg03811012010-12-08 00:21:06 +0200752 goto failed;
753 }
754
755 cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len);
756 if (!cmd) {
757 err = -ENOMEM;
758 goto failed;
759 }
760
761 if (cp->val)
Gustavo F. Padovan7f971042011-12-18 12:40:32 -0200762 schedule_work(&hdev->power_on);
Johan Hedberg03811012010-12-08 00:21:06 +0200763 else
Gustavo F. Padovan80b7ab32011-12-17 14:52:27 -0200764 schedule_work(&hdev->power_off.work);
Johan Hedberg03811012010-12-08 00:21:06 +0200765
766 err = 0;
767
768failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300769 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200770 hci_dev_put(hdev);
771 return err;
772}
773
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300774static int set_discoverable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200775{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300776 struct mgmt_cp_set_discoverable *cp = data;
Johan Hedberge41d8b42010-12-13 21:07:03 +0200777 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +0200778 struct pending_cmd *cmd;
779 u8 scan;
780 int err;
781
Johan Hedberg03811012010-12-08 00:21:06 +0200782 BT_DBG("request for hci%u", index);
783
784 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200785 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
786 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg03811012010-12-08 00:21:06 +0200787
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200788 hdev = hci_dev_get(index);
789 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200790 return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
791 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200792
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300793 hci_dev_lock(hdev);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200794
795 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200796 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
797 MGMT_STATUS_NOT_POWERED);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200798 goto failed;
799 }
800
801 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
802 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200803 err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE,
804 MGMT_STATUS_BUSY);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200805 goto failed;
806 }
807
808 if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
809 test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200810 err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev);
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200811 goto failed;
812 }
813
814 cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len);
815 if (!cmd) {
816 err = -ENOMEM;
817 goto failed;
818 }
819
820 scan = SCAN_PAGE;
821
822 if (cp->val)
823 scan |= SCAN_INQUIRY;
824 else
825 cancel_delayed_work(&hdev->discov_off);
826
827 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
828 if (err < 0)
829 mgmt_pending_remove(cmd);
830
Johan Hedberg03811012010-12-08 00:21:06 +0200831 if (cp->val)
832 hdev->discov_timeout = get_unaligned_le16(&cp->timeout);
833
Johan Hedberge41d8b42010-12-13 21:07:03 +0200834failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300835 hci_dev_unlock(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200836 hci_dev_put(hdev);
837
838 return err;
839}
840
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300841static int set_connectable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200842{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300843 struct mgmt_mode *cp = data;
Johan Hedberg03811012010-12-08 00:21:06 +0200844 struct hci_dev *hdev;
845 struct pending_cmd *cmd;
846 u8 scan;
847 int err;
848
Johan Hedberge41d8b42010-12-13 21:07:03 +0200849 BT_DBG("request for hci%u", index);
850
Johan Hedberg03811012010-12-08 00:21:06 +0200851 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200852 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
853 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200854
855 hdev = hci_dev_get(index);
856 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200857 return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
858 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200859
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300860 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200861
862 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200863 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
864 MGMT_STATUS_NOT_POWERED);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200865 goto failed;
866 }
867
868 if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) ||
869 mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +0200870 err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE,
871 MGMT_STATUS_BUSY);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200872 goto failed;
873 }
874
875 if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) {
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200876 err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200877 goto failed;
878 }
879
880 cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len);
881 if (!cmd) {
882 err = -ENOMEM;
883 goto failed;
884 }
885
886 if (cp->val)
887 scan = SCAN_PAGE;
888 else
889 scan = 0;
890
891 err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
892 if (err < 0)
893 mgmt_pending_remove(cmd);
894
895failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300896 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200897 hci_dev_put(hdev);
898
899 return err;
900}
901
902static int mgmt_event(u16 event, struct hci_dev *hdev, void *data,
903 u16 data_len, struct sock *skip_sk)
904{
905 struct sk_buff *skb;
906 struct mgmt_hdr *hdr;
907
908 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
909 if (!skb)
910 return -ENOMEM;
911
912 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
913
914 hdr = (void *) skb_put(skb, sizeof(*hdr));
915 hdr->opcode = cpu_to_le16(event);
916 if (hdev)
917 hdr->index = cpu_to_le16(hdev->id);
918 else
919 hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
920 hdr->len = cpu_to_le16(data_len);
921
922 if (data)
923 memcpy(skb_put(skb, data_len), data, data_len);
924
925 hci_send_to_sock(NULL, skb, skip_sk);
926 kfree_skb(skb);
927
928 return 0;
929}
930
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300931static int set_pairable(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg73f22f62010-12-29 16:00:25 +0200932{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -0300933 struct mgmt_mode *cp = data;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200934 struct hci_dev *hdev;
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200935 __le32 ev;
Johan Hedberg73f22f62010-12-29 16:00:25 +0200936 int err;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200937
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200938 BT_DBG("request for hci%u", index);
939
940 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +0200941 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
942 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200943
944 hdev = hci_dev_get(index);
945 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +0200946 return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE,
947 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200948
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300949 hci_dev_lock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200950
951 if (cp->val)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200952 set_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200953 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +0200954 clear_bit(HCI_PAIRABLE, &hdev->dev_flags);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200955
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200956 err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200957 if (err < 0)
958 goto failed;
959
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200960 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200961
Johan Hedberg69ab39e2011-12-15 00:47:35 +0200962 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), sk);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200963
964failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -0300965 hci_dev_unlock(hdev);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +0200966 hci_dev_put(hdev);
967
968 return err;
969}
Johan Hedberg72a734e2010-12-30 00:38:22 +0200970
Johan Hedberg33ef95e2012-02-16 23:56:27 +0200971static int set_link_security(struct sock *sk, u16 index, void *data, u16 len)
972{
973 struct mgmt_mode *cp = data;
974 struct pending_cmd *cmd;
975 struct hci_dev *hdev;
976 uint8_t val;
977 int err;
978
979 BT_DBG("request for hci%u", index);
980
981 if (len != sizeof(*cp))
982 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
983 MGMT_STATUS_INVALID_PARAMS);
984
985 hdev = hci_dev_get(index);
986 if (!hdev)
987 return cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
988 MGMT_STATUS_INVALID_PARAMS);
989
990 hci_dev_lock(hdev);
991
992 if (!test_bit(HCI_UP, &hdev->flags)) {
993 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
994 MGMT_STATUS_NOT_POWERED);
995 goto failed;
996 }
997
998 if (mgmt_pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) {
999 err = cmd_status(sk, index, MGMT_OP_SET_LINK_SECURITY,
1000 MGMT_STATUS_BUSY);
1001 goto failed;
1002 }
1003
1004 val = !!cp->val;
1005
1006 if (test_bit(HCI_AUTH, &hdev->flags) == val) {
1007 err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev);
1008 goto failed;
1009 }
1010
1011 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len);
1012 if (!cmd) {
1013 err = -ENOMEM;
1014 goto failed;
1015 }
1016
1017 err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val);
1018 if (err < 0) {
1019 mgmt_pending_remove(cmd);
1020 goto failed;
1021 }
1022
1023failed:
1024 hci_dev_unlock(hdev);
1025 hci_dev_put(hdev);
1026
1027 return err;
1028}
1029
Johan Hedberged2c4ee2012-02-17 00:56:28 +02001030static int set_ssp(struct sock *sk, u16 index, void *data, u16 len)
1031{
1032 struct mgmt_mode *cp = data;
1033 struct pending_cmd *cmd;
1034 struct hci_dev *hdev;
1035 uint8_t val;
1036 int err;
1037
1038 BT_DBG("request for hci%u", index);
1039
1040 if (len != sizeof(*cp))
1041 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1042 MGMT_STATUS_INVALID_PARAMS);
1043
1044 hdev = hci_dev_get(index);
1045 if (!hdev)
1046 return cmd_status(sk, index, MGMT_OP_SET_SSP,
1047 MGMT_STATUS_INVALID_PARAMS);
1048
1049 hci_dev_lock(hdev);
1050
1051 if (!test_bit(HCI_UP, &hdev->flags)) {
1052 err = cmd_status(sk, index, MGMT_OP_SET_SSP,
1053 MGMT_STATUS_NOT_POWERED);
1054 goto failed;
1055 }
1056
1057 if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) {
1058 err = cmd_status(sk, index, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY);
1059 goto failed;
1060 }
1061
1062 val = !!cp->val;
1063
1064 if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) {
1065 err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev);
1066 goto failed;
1067 }
1068
1069 cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len);
1070 if (!cmd) {
1071 err = -ENOMEM;
1072 goto failed;
1073 }
1074
1075 err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val);
1076 if (err < 0) {
1077 mgmt_pending_remove(cmd);
1078 goto failed;
1079 }
1080
1081failed:
1082 hci_dev_unlock(hdev);
1083 hci_dev_put(hdev);
1084
1085 return err;
1086}
1087
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001088static int add_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001089{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001090 struct mgmt_cp_add_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001091 struct hci_dev *hdev;
1092 struct bt_uuid *uuid;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001093 int err;
1094
Szymon Janc4e51eae2011-02-25 19:05:48 +01001095 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001096
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001097 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001098 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1099 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001100
Szymon Janc4e51eae2011-02-25 19:05:48 +01001101 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001102 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001103 return cmd_status(sk, index, MGMT_OP_ADD_UUID,
1104 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001105
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001106 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001107
1108 uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
1109 if (!uuid) {
1110 err = -ENOMEM;
1111 goto failed;
1112 }
1113
1114 memcpy(uuid->uuid, cp->uuid, 16);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001115 uuid->svc_hint = cp->svc_hint;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001116
1117 list_add(&uuid->list, &hdev->uuids);
1118
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001119 err = update_class(hdev);
1120 if (err < 0)
1121 goto failed;
1122
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001123 err = update_eir(hdev);
1124 if (err < 0)
1125 goto failed;
1126
Szymon Janc4e51eae2011-02-25 19:05:48 +01001127 err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001128
1129failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001130 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001131 hci_dev_put(hdev);
1132
1133 return err;
1134}
1135
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001136static int remove_uuid(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001137{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001138 struct mgmt_cp_remove_uuid *cp = data;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001139 struct list_head *p, *n;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001140 struct hci_dev *hdev;
1141 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 +02001142 int err, found;
1143
Szymon Janc4e51eae2011-02-25 19:05:48 +01001144 BT_DBG("request for hci%u", index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001145
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001146 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001147 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1148 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001149
Szymon Janc4e51eae2011-02-25 19:05:48 +01001150 hdev = hci_dev_get(index);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001151 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001152 return cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1153 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001154
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001155 hci_dev_lock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001156
1157 if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
1158 err = hci_uuids_clear(hdev);
1159 goto unlock;
1160 }
1161
1162 found = 0;
1163
1164 list_for_each_safe(p, n, &hdev->uuids) {
1165 struct bt_uuid *match = list_entry(p, struct bt_uuid, list);
1166
1167 if (memcmp(match->uuid, cp->uuid, 16) != 0)
1168 continue;
1169
1170 list_del(&match->list);
1171 found++;
1172 }
1173
1174 if (found == 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001175 err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID,
1176 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001177 goto unlock;
1178 }
1179
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001180 err = update_class(hdev);
1181 if (err < 0)
1182 goto unlock;
1183
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03001184 err = update_eir(hdev);
1185 if (err < 0)
1186 goto unlock;
1187
Szymon Janc4e51eae2011-02-25 19:05:48 +01001188 err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001189
1190unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001191 hci_dev_unlock(hdev);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02001192 hci_dev_put(hdev);
1193
1194 return err;
1195}
1196
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001197static int set_dev_class(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001198{
1199 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001200 struct mgmt_cp_set_dev_class *cp = data;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001201 int err;
1202
Szymon Janc4e51eae2011-02-25 19:05:48 +01001203 BT_DBG("request for hci%u", index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001204
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001205 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001206 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1207 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001208
Szymon Janc4e51eae2011-02-25 19:05:48 +01001209 hdev = hci_dev_get(index);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001210 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001211 return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS,
1212 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001213
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001214 hci_dev_lock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001215
1216 hdev->major_class = cp->major;
1217 hdev->minor_class = cp->minor;
1218
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001219 if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) {
Johan Hedberg7d785252011-12-15 00:47:39 +02001220 hci_dev_unlock(hdev);
1221 cancel_delayed_work_sync(&hdev->service_cache);
1222 hci_dev_lock(hdev);
Johan Hedberg14c0b602011-12-15 00:47:37 +02001223 update_eir(hdev);
Johan Hedberg7d785252011-12-15 00:47:39 +02001224 }
Johan Hedberg14c0b602011-12-15 00:47:37 +02001225
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001226 err = update_class(hdev);
1227
1228 if (err == 0)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001229 err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001230
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001231 hci_dev_unlock(hdev);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02001232 hci_dev_put(hdev);
1233
1234 return err;
1235}
1236
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001237static int load_link_keys(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001238{
1239 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001240 struct mgmt_cp_load_link_keys *cp = data;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001241 u16 key_count, expected_len;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001242 int i;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001243
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001244 if (len < sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001245 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1246 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001247
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001248 key_count = get_unaligned_le16(&cp->key_count);
1249
Johan Hedberg86742e12011-11-07 23:13:38 +02001250 expected_len = sizeof(*cp) + key_count *
1251 sizeof(struct mgmt_link_key_info);
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001252 if (expected_len != len) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001253 BT_ERR("load_link_keys: expected %u bytes, got %u bytes",
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001254 len, expected_len);
Johan Hedbergca69b792011-11-11 18:10:00 +02001255 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1256 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001257 }
1258
Szymon Janc4e51eae2011-02-25 19:05:48 +01001259 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001260 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001261 return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS,
1262 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001263
Szymon Janc4e51eae2011-02-25 19:05:48 +01001264 BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001265 key_count);
1266
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001267 hci_dev_lock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001268
1269 hci_link_keys_clear(hdev);
1270
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001271 set_bit(HCI_LINK_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001272
1273 if (cp->debug_keys)
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001274 set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001275 else
Johan Hedberga8b2d5c2012-01-08 23:11:15 +02001276 clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001277
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001278 for (i = 0; i < key_count; i++) {
Johan Hedberg86742e12011-11-07 23:13:38 +02001279 struct mgmt_link_key_info *key = &cp->keys[i];
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001280
Johan Hedbergd753fdc2012-02-17 14:06:34 +02001281 hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val,
1282 key->type, key->pin_len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001283 }
1284
Johan Hedberg0e5f8752011-11-11 16:18:54 +02001285 cmd_complete(sk, index, MGMT_OP_LOAD_LINK_KEYS, NULL, 0);
1286
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001287 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001288 hci_dev_put(hdev);
1289
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03001290 return 0;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001291}
1292
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001293static int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr,
1294 u8 addr_type, struct sock *skip_sk)
1295{
1296 struct mgmt_ev_device_unpaired ev;
1297
1298 bacpy(&ev.addr.bdaddr, bdaddr);
1299 ev.addr.type = addr_type;
1300
1301 return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev),
1302 skip_sk);
1303}
1304
Johan Hedberg124f6e32012-02-09 13:50:12 +02001305static int unpair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001306{
1307 struct hci_dev *hdev;
Johan Hedberg124f6e32012-02-09 13:50:12 +02001308 struct mgmt_cp_unpair_device *cp = data;
1309 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001310 struct hci_cp_disconnect dc;
1311 struct pending_cmd *cmd;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001312 struct hci_conn *conn;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001313 int err;
1314
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001315 if (len != sizeof(*cp))
Johan Hedberg124f6e32012-02-09 13:50:12 +02001316 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001317 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001318
Szymon Janc4e51eae2011-02-25 19:05:48 +01001319 hdev = hci_dev_get(index);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001320 if (!hdev)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001321 return cmd_status(sk, index, MGMT_OP_UNPAIR_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02001322 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001323
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001324 hci_dev_lock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001325
Johan Hedberga8a1d192011-11-10 15:54:38 +02001326 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02001327 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1328 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02001329
Johan Hedberg124f6e32012-02-09 13:50:12 +02001330 if (cp->addr.type == MGMT_ADDR_BREDR)
1331 err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
1332 else
1333 err = hci_remove_ltk(hdev, &cp->addr.bdaddr);
Vinicius Costa Gomesb0dbfb42012-02-02 21:08:03 -03001334
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001335 if (err < 0) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001336 rp.status = MGMT_STATUS_NOT_PAIRED;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001337 goto unlock;
1338 }
1339
Johan Hedberga8a1d192011-11-10 15:54:38 +02001340 if (!test_bit(HCI_UP, &hdev->flags) || !cp->disconnect) {
Johan Hedberg124f6e32012-02-09 13:50:12 +02001341 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001342 sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001343 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001344 goto unlock;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001345 }
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001346
Johan Hedberg124f6e32012-02-09 13:50:12 +02001347 if (cp->addr.type == MGMT_ADDR_BREDR)
1348 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
1349 &cp->addr.bdaddr);
1350 else
1351 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
1352 &cp->addr.bdaddr);
1353
Johan Hedberga8a1d192011-11-10 15:54:38 +02001354 if (!conn) {
Johan Hedberg124f6e32012-02-09 13:50:12 +02001355 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001356 sizeof(rp));
Johan Hedbergb1078ad2012-02-09 17:21:16 +02001357 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk);
Johan Hedberga8a1d192011-11-10 15:54:38 +02001358 goto unlock;
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001359 }
1360
Johan Hedberg124f6e32012-02-09 13:50:12 +02001361 cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp,
1362 sizeof(*cp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02001363 if (!cmd) {
1364 err = -ENOMEM;
1365 goto unlock;
1366 }
1367
1368 put_unaligned_le16(conn->handle, &dc.handle);
1369 dc.reason = 0x13; /* Remote User Terminated Connection */
1370 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1371 if (err < 0)
1372 mgmt_pending_remove(cmd);
1373
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001374unlock:
Johan Hedbergca69b792011-11-11 18:10:00 +02001375 if (err < 0)
Johan Hedberg124f6e32012-02-09 13:50:12 +02001376 err = cmd_complete(sk, index, MGMT_OP_UNPAIR_DEVICE, &rp,
Johan Hedberga8a1d192011-11-10 15:54:38 +02001377 sizeof(rp));
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001378 hci_dev_unlock(hdev);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02001379 hci_dev_put(hdev);
1380
1381 return err;
1382}
1383
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001384static int disconnect(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg8962ee72011-01-20 12:40:27 +02001385{
1386 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001387 struct mgmt_cp_disconnect *cp = data;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001388 struct hci_cp_disconnect dc;
Johan Hedberg366a0332011-02-19 12:05:55 -03001389 struct pending_cmd *cmd;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001390 struct hci_conn *conn;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001391 int err;
1392
1393 BT_DBG("");
1394
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001395 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001396 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1397 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001398
Szymon Janc4e51eae2011-02-25 19:05:48 +01001399 hdev = hci_dev_get(index);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001400 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001401 return cmd_status(sk, index, MGMT_OP_DISCONNECT,
1402 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001403
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001404 hci_dev_lock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001405
1406 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001407 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1408 MGMT_STATUS_NOT_POWERED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001409 goto failed;
1410 }
1411
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001412 if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001413 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1414 MGMT_STATUS_BUSY);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001415 goto failed;
1416 }
1417
Johan Hedberg88c3df12012-02-09 14:27:38 +02001418 if (cp->addr.type == MGMT_ADDR_BREDR)
1419 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
1420 else
1421 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr);
Vinicius Costa Gomes365227e2011-05-06 18:41:44 -03001422
Johan Hedberg8962ee72011-01-20 12:40:27 +02001423 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001424 err = cmd_status(sk, index, MGMT_OP_DISCONNECT,
1425 MGMT_STATUS_NOT_CONNECTED);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001426 goto failed;
1427 }
1428
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001429 cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001430 if (!cmd) {
1431 err = -ENOMEM;
Johan Hedberg8962ee72011-01-20 12:40:27 +02001432 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001433 }
Johan Hedberg8962ee72011-01-20 12:40:27 +02001434
1435 put_unaligned_le16(conn->handle, &dc.handle);
1436 dc.reason = 0x13; /* Remote User Terminated Connection */
1437
1438 err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
1439 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001440 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001441
1442failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001443 hci_dev_unlock(hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02001444 hci_dev_put(hdev);
1445
1446 return err;
1447}
1448
Johan Hedberg48264f02011-11-09 13:58:58 +02001449static u8 link_to_mgmt(u8 link_type, u8 addr_type)
Johan Hedberg4c659c32011-11-07 23:13:39 +02001450{
1451 switch (link_type) {
1452 case LE_LINK:
Johan Hedberg48264f02011-11-09 13:58:58 +02001453 switch (addr_type) {
1454 case ADDR_LE_DEV_PUBLIC:
1455 return MGMT_ADDR_LE_PUBLIC;
1456 case ADDR_LE_DEV_RANDOM:
1457 return MGMT_ADDR_LE_RANDOM;
1458 default:
1459 return MGMT_ADDR_INVALID;
1460 }
Johan Hedberg4c659c32011-11-07 23:13:39 +02001461 case ACL_LINK:
1462 return MGMT_ADDR_BREDR;
1463 default:
1464 return MGMT_ADDR_INVALID;
1465 }
1466}
1467
Szymon Janc8ce62842011-03-01 16:55:32 +01001468static int get_connections(struct sock *sk, u16 index)
Johan Hedberg2784eb42011-01-21 13:56:35 +02001469{
Johan Hedberg2784eb42011-01-21 13:56:35 +02001470 struct mgmt_rp_get_connections *rp;
1471 struct hci_dev *hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001472 struct hci_conn *c;
Johan Hedberga38528f2011-01-22 06:46:43 +02001473 size_t rp_len;
Szymon Janc4e51eae2011-02-25 19:05:48 +01001474 u16 count;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001475 int i, err;
1476
1477 BT_DBG("");
1478
Szymon Janc4e51eae2011-02-25 19:05:48 +01001479 hdev = hci_dev_get(index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001480 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001481 return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS,
1482 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001483
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001484 hci_dev_lock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001485
1486 count = 0;
Johan Hedbergb644ba32012-01-17 21:48:47 +02001487 list_for_each_entry(c, &hdev->conn_hash.list, list) {
1488 if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1489 count++;
Johan Hedberg2784eb42011-01-21 13:56:35 +02001490 }
1491
Johan Hedberg4c659c32011-11-07 23:13:39 +02001492 rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info));
Johan Hedberga38528f2011-01-22 06:46:43 +02001493 rp = kmalloc(rp_len, GFP_ATOMIC);
1494 if (!rp) {
Johan Hedberg2784eb42011-01-21 13:56:35 +02001495 err = -ENOMEM;
1496 goto unlock;
1497 }
1498
Johan Hedberg2784eb42011-01-21 13:56:35 +02001499 put_unaligned_le16(count, &rp->conn_count);
1500
Johan Hedberg2784eb42011-01-21 13:56:35 +02001501 i = 0;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001502 list_for_each_entry(c, &hdev->conn_hash.list, list) {
Johan Hedbergb644ba32012-01-17 21:48:47 +02001503 if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags))
1504 continue;
Johan Hedberg4c659c32011-11-07 23:13:39 +02001505 bacpy(&rp->addr[i].bdaddr, &c->dst);
Johan Hedberg48264f02011-11-09 13:58:58 +02001506 rp->addr[i].type = link_to_mgmt(c->type, c->dst_type);
Johan Hedberg4c659c32011-11-07 23:13:39 +02001507 if (rp->addr[i].type == MGMT_ADDR_INVALID)
1508 continue;
1509 i++;
1510 }
1511
1512 /* Recalculate length in case of filtered SCO connections, etc */
1513 rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info));
Johan Hedberg2784eb42011-01-21 13:56:35 +02001514
Szymon Janc4e51eae2011-02-25 19:05:48 +01001515 err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001516
1517unlock:
Johan Hedberga38528f2011-01-22 06:46:43 +02001518 kfree(rp);
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001519 hci_dev_unlock(hdev);
Johan Hedberg2784eb42011-01-21 13:56:35 +02001520 hci_dev_put(hdev);
1521 return err;
1522}
1523
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001524static int send_pin_code_neg_reply(struct sock *sk, u16 index,
1525 struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
1526{
1527 struct pending_cmd *cmd;
1528 int err;
1529
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001530 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp,
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001531 sizeof(*cp));
1532 if (!cmd)
1533 return -ENOMEM;
1534
Johan Hedbergd8457692012-02-17 14:24:57 +02001535 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY,
1536 sizeof(cp->addr.bdaddr), &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001537 if (err < 0)
1538 mgmt_pending_remove(cmd);
1539
1540 return err;
1541}
1542
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001543static int pin_code_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001544{
1545 struct hci_dev *hdev;
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001546 struct hci_conn *conn;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001547 struct mgmt_cp_pin_code_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001548 struct hci_cp_pin_code_reply reply;
Johan Hedberg366a0332011-02-19 12:05:55 -03001549 struct pending_cmd *cmd;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001550 int err;
1551
1552 BT_DBG("");
1553
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001554 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001555 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1556 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001557
Szymon Janc4e51eae2011-02-25 19:05:48 +01001558 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001559 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001560 return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1561 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001562
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001563 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001564
1565 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001566 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1567 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001568 goto failed;
1569 }
1570
Johan Hedbergd8457692012-02-17 14:24:57 +02001571 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001572 if (!conn) {
Johan Hedbergca69b792011-11-11 18:10:00 +02001573 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
1574 MGMT_STATUS_NOT_CONNECTED);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001575 goto failed;
1576 }
1577
1578 if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
Johan Hedbergd8457692012-02-17 14:24:57 +02001579 struct mgmt_cp_pin_code_neg_reply ncp;
1580
1581 memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr));
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001582
1583 BT_ERR("PIN code is not 16 bytes long");
1584
1585 err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
1586 if (err >= 0)
1587 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001588 MGMT_STATUS_INVALID_PARAMS);
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001589
1590 goto failed;
1591 }
1592
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001593 cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data,
1594 len);
Johan Hedberg366a0332011-02-19 12:05:55 -03001595 if (!cmd) {
1596 err = -ENOMEM;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001597 goto failed;
Johan Hedberg366a0332011-02-19 12:05:55 -03001598 }
Johan Hedberg980e1a52011-01-22 06:10:07 +02001599
Johan Hedbergd8457692012-02-17 14:24:57 +02001600 bacpy(&reply.bdaddr, &cp->addr.bdaddr);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001601 reply.pin_len = cp->pin_len;
Waldemar Rymarkiewicz24718ca2011-06-01 17:28:47 +02001602 memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
Johan Hedberg980e1a52011-01-22 06:10:07 +02001603
1604 err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
1605 if (err < 0)
Johan Hedberga664b5b2011-02-19 12:06:02 -03001606 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001607
1608failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001609 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001610 hci_dev_put(hdev);
1611
1612 return err;
1613}
1614
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001615static int pin_code_neg_reply(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg980e1a52011-01-22 06:10:07 +02001616{
1617 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001618 struct mgmt_cp_pin_code_neg_reply *cp = data;
Johan Hedberg980e1a52011-01-22 06:10:07 +02001619 int err;
1620
1621 BT_DBG("");
1622
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001623 if (len != sizeof(*cp))
1624 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001625 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001626
Szymon Janc4e51eae2011-02-25 19:05:48 +01001627 hdev = hci_dev_get(index);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001628 if (!hdev)
Szymon Janc4e51eae2011-02-25 19:05:48 +01001629 return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001630 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001631
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001632 hci_dev_lock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001633
1634 if (!test_bit(HCI_UP, &hdev->flags)) {
Szymon Janc4e51eae2011-02-25 19:05:48 +01001635 err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
Johan Hedbergca69b792011-11-11 18:10:00 +02001636 MGMT_STATUS_NOT_POWERED);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001637 goto failed;
1638 }
1639
Waldemar Rymarkiewicz96d97a62011-06-01 17:28:48 +02001640 err = send_pin_code_neg_reply(sk, index, hdev, cp);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001641
1642failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001643 hci_dev_unlock(hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02001644 hci_dev_put(hdev);
1645
1646 return err;
1647}
1648
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001649static int set_io_capability(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001650{
1651 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001652 struct mgmt_cp_set_io_capability *cp = data;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001653
1654 BT_DBG("");
1655
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001656 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001657 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1658 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001659
Szymon Janc4e51eae2011-02-25 19:05:48 +01001660 hdev = hci_dev_get(index);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001661 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001662 return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY,
1663 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001664
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001665 hci_dev_lock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001666
1667 hdev->io_capability = cp->io_capability;
1668
1669 BT_DBG("%s IO capability set to 0x%02x", hdev->name,
Szymon Jancb8534e02011-03-01 16:55:34 +01001670 hdev->io_capability);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001671
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001672 hci_dev_unlock(hdev);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001673 hci_dev_put(hdev);
1674
Szymon Janc4e51eae2011-02-25 19:05:48 +01001675 return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02001676}
1677
Johan Hedberge9a416b2011-02-19 12:05:56 -03001678static inline struct pending_cmd *find_pairing(struct hci_conn *conn)
1679{
1680 struct hci_dev *hdev = conn->hdev;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +02001681 struct pending_cmd *cmd;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001682
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001683 list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
Johan Hedberge9a416b2011-02-19 12:05:56 -03001684 if (cmd->opcode != MGMT_OP_PAIR_DEVICE)
1685 continue;
1686
Johan Hedberge9a416b2011-02-19 12:05:56 -03001687 if (cmd->user_data != conn)
1688 continue;
1689
1690 return cmd;
1691 }
1692
1693 return NULL;
1694}
1695
1696static void pairing_complete(struct pending_cmd *cmd, u8 status)
1697{
1698 struct mgmt_rp_pair_device rp;
1699 struct hci_conn *conn = cmd->user_data;
1700
Johan Hedbergba4e5642011-11-11 00:07:34 +02001701 bacpy(&rp.addr.bdaddr, &conn->dst);
1702 rp.addr.type = link_to_mgmt(conn->type, conn->dst_type);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001703 rp.status = status;
1704
Szymon Janc4e51eae2011-02-25 19:05:48 +01001705 cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001706
1707 /* So we don't get further callbacks for this connection */
1708 conn->connect_cfm_cb = NULL;
1709 conn->security_cfm_cb = NULL;
1710 conn->disconn_cfm_cb = NULL;
1711
1712 hci_conn_put(conn);
1713
Johan Hedberga664b5b2011-02-19 12:06:02 -03001714 mgmt_pending_remove(cmd);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001715}
1716
1717static void pairing_complete_cb(struct hci_conn *conn, u8 status)
1718{
1719 struct pending_cmd *cmd;
1720
1721 BT_DBG("status %u", status);
1722
Johan Hedberg56e5cb82011-11-08 20:40:16 +02001723 cmd = find_pairing(conn);
1724 if (!cmd)
1725 BT_DBG("Unable to find a pending command");
1726 else
1727 pairing_complete(cmd, status);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001728}
1729
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001730static int pair_device(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001731{
1732 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001733 struct mgmt_cp_pair_device *cp = data;
Johan Hedberg1425acb2011-11-11 00:07:35 +02001734 struct mgmt_rp_pair_device rp;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001735 struct pending_cmd *cmd;
1736 u8 sec_level, auth_type;
1737 struct hci_conn *conn;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001738 int err;
1739
1740 BT_DBG("");
1741
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001742 if (len != sizeof(*cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02001743 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1744 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancbdce7ba2011-02-25 19:05:49 +01001745
Szymon Janc4e51eae2011-02-25 19:05:48 +01001746 hdev = hci_dev_get(index);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001747 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001748 return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE,
1749 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001750
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001751 hci_dev_lock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001752
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001753 sec_level = BT_SECURITY_MEDIUM;
1754 if (cp->io_cap == 0x03)
Johan Hedberge9a416b2011-02-19 12:05:56 -03001755 auth_type = HCI_AT_DEDICATED_BONDING;
Vinicius Costa Gomesc908df32011-09-02 14:51:22 -03001756 else
Johan Hedberge9a416b2011-02-19 12:05:56 -03001757 auth_type = HCI_AT_DEDICATED_BONDING_MITM;
Johan Hedberge9a416b2011-02-19 12:05:56 -03001758
Johan Hedbergba4e5642011-11-11 00:07:34 +02001759 if (cp->addr.type == MGMT_ADDR_BREDR)
1760 conn = hci_connect(hdev, ACL_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001761 auth_type);
1762 else
Johan Hedbergba4e5642011-11-11 00:07:34 +02001763 conn = hci_connect(hdev, LE_LINK, &cp->addr.bdaddr, sec_level,
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001764 auth_type);
1765
Johan Hedberg1425acb2011-11-11 00:07:35 +02001766 memset(&rp, 0, sizeof(rp));
1767 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
1768 rp.addr.type = cp->addr.type;
1769
Ville Tervo30e76272011-02-22 16:10:53 -03001770 if (IS_ERR(conn)) {
Johan Hedberg1425acb2011-11-11 00:07:35 +02001771 rp.status = -PTR_ERR(conn);
1772 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1773 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001774 goto unlock;
1775 }
1776
1777 if (conn->connect_cfm_cb) {
1778 hci_conn_put(conn);
Johan Hedberg1425acb2011-11-11 00:07:35 +02001779 rp.status = EBUSY;
1780 err = cmd_complete(sk, index, MGMT_OP_PAIR_DEVICE,
1781 &rp, sizeof(rp));
Johan Hedberge9a416b2011-02-19 12:05:56 -03001782 goto unlock;
1783 }
1784
Johan Hedberg2e58ef32011-11-08 20:40:15 +02001785 cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001786 if (!cmd) {
1787 err = -ENOMEM;
1788 hci_conn_put(conn);
1789 goto unlock;
1790 }
1791
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001792 /* For LE, just connecting isn't a proof that the pairing finished */
Johan Hedbergba4e5642011-11-11 00:07:34 +02001793 if (cp->addr.type == MGMT_ADDR_BREDR)
Vinicius Costa Gomes7a512d02011-08-19 21:06:54 -03001794 conn->connect_cfm_cb = pairing_complete_cb;
1795
Johan Hedberge9a416b2011-02-19 12:05:56 -03001796 conn->security_cfm_cb = pairing_complete_cb;
1797 conn->disconn_cfm_cb = pairing_complete_cb;
1798 conn->io_capability = cp->io_cap;
1799 cmd->user_data = conn;
1800
1801 if (conn->state == BT_CONNECTED &&
1802 hci_conn_security(conn, sec_level, auth_type))
1803 pairing_complete(cmd, 0);
1804
1805 err = 0;
1806
1807unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001808 hci_dev_unlock(hdev);
Johan Hedberge9a416b2011-02-19 12:05:56 -03001809 hci_dev_put(hdev);
1810
1811 return err;
1812}
1813
Johan Hedberg28424702012-02-02 04:02:29 +02001814static int cancel_pair_device(struct sock *sk, u16 index,
1815 unsigned char *data, u16 len)
1816{
1817 struct mgmt_addr_info *addr = (void *) data;
1818 struct hci_dev *hdev;
1819 struct pending_cmd *cmd;
1820 struct hci_conn *conn;
1821 int err;
1822
1823 BT_DBG("");
1824
1825 if (len != sizeof(*addr))
1826 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1827 MGMT_STATUS_INVALID_PARAMS);
1828
1829 hdev = hci_dev_get(index);
1830 if (!hdev)
1831 return cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1832 MGMT_STATUS_INVALID_PARAMS);
1833
1834 hci_dev_lock(hdev);
1835
1836 cmd = mgmt_pending_find(MGMT_OP_PAIR_DEVICE, hdev);
1837 if (!cmd) {
1838 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1839 MGMT_STATUS_INVALID_PARAMS);
1840 goto unlock;
1841 }
1842
1843 conn = cmd->user_data;
1844
1845 if (bacmp(&addr->bdaddr, &conn->dst) != 0) {
1846 err = cmd_status(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE,
1847 MGMT_STATUS_INVALID_PARAMS);
1848 goto unlock;
1849 }
1850
1851 pairing_complete(cmd, MGMT_STATUS_CANCELLED);
1852
1853 err = cmd_complete(sk, index, MGMT_OP_CANCEL_PAIR_DEVICE, addr,
1854 sizeof(*addr));
1855unlock:
1856 hci_dev_unlock(hdev);
1857 hci_dev_put(hdev);
1858
1859 return err;
1860}
1861
Brian Gix0df4c182011-11-16 13:53:13 -08001862static int user_pairing_resp(struct sock *sk, u16 index, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02001863 u8 type, u16 mgmt_op, u16 hci_op,
1864 __le32 passkey)
Johan Hedberga5c29682011-02-19 12:05:57 -03001865{
Johan Hedberga5c29682011-02-19 12:05:57 -03001866 struct pending_cmd *cmd;
1867 struct hci_dev *hdev;
Brian Gix0df4c182011-11-16 13:53:13 -08001868 struct hci_conn *conn;
Johan Hedberga5c29682011-02-19 12:05:57 -03001869 int err;
1870
Szymon Janc4e51eae2011-02-25 19:05:48 +01001871 hdev = hci_dev_get(index);
Johan Hedberga5c29682011-02-19 12:05:57 -03001872 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02001873 return cmd_status(sk, index, mgmt_op,
1874 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberga5c29682011-02-19 12:05:57 -03001875
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001876 hci_dev_lock(hdev);
Johan Hedberg08ba5382011-03-16 14:29:34 +02001877
Johan Hedberga5c29682011-02-19 12:05:57 -03001878 if (!test_bit(HCI_UP, &hdev->flags)) {
Brian Gix0df4c182011-11-16 13:53:13 -08001879 err = cmd_status(sk, index, mgmt_op, MGMT_STATUS_NOT_POWERED);
1880 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001881 }
1882
Johan Hedberg272d90d2012-02-09 15:26:12 +02001883 if (type == MGMT_ADDR_BREDR)
1884 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr);
1885 else
Brian Gix47c15e22011-11-16 13:53:14 -08001886 conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr);
Brian Gix47c15e22011-11-16 13:53:14 -08001887
Johan Hedberg272d90d2012-02-09 15:26:12 +02001888 if (!conn) {
1889 err = cmd_status(sk, index, mgmt_op,
1890 MGMT_STATUS_NOT_CONNECTED);
1891 goto done;
1892 }
1893
1894 if (type == MGMT_ADDR_LE_PUBLIC || type == MGMT_ADDR_LE_RANDOM) {
Brian Gix47c15e22011-11-16 13:53:14 -08001895 /* Continue with pairing via SMP */
Brian Gix5fe57d92011-12-21 16:12:13 -08001896 err = smp_user_confirm_reply(conn, mgmt_op, passkey);
Brian Gix47c15e22011-11-16 13:53:14 -08001897
Brian Gix5fe57d92011-12-21 16:12:13 -08001898 if (!err)
1899 err = cmd_status(sk, index, mgmt_op,
1900 MGMT_STATUS_SUCCESS);
1901 else
1902 err = cmd_status(sk, index, mgmt_op,
1903 MGMT_STATUS_FAILED);
1904
Brian Gix47c15e22011-11-16 13:53:14 -08001905 goto done;
1906 }
1907
Brian Gix0df4c182011-11-16 13:53:13 -08001908 cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr));
Johan Hedberga5c29682011-02-19 12:05:57 -03001909 if (!cmd) {
1910 err = -ENOMEM;
Brian Gix0df4c182011-11-16 13:53:13 -08001911 goto done;
Johan Hedberga5c29682011-02-19 12:05:57 -03001912 }
1913
Brian Gix0df4c182011-11-16 13:53:13 -08001914 /* Continue with pairing via HCI */
Brian Gix604086b2011-11-23 08:28:33 -08001915 if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
1916 struct hci_cp_user_passkey_reply cp;
1917
1918 bacpy(&cp.bdaddr, bdaddr);
1919 cp.passkey = passkey;
1920 err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp);
1921 } else
1922 err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr);
1923
Johan Hedberga664b5b2011-02-19 12:06:02 -03001924 if (err < 0)
1925 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03001926
Brian Gix0df4c182011-11-16 13:53:13 -08001927done:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03001928 hci_dev_unlock(hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03001929 hci_dev_put(hdev);
1930
1931 return err;
1932}
1933
Brian Gix0df4c182011-11-16 13:53:13 -08001934static int user_confirm_reply(struct sock *sk, u16 index, void *data, u16 len)
1935{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001936 struct mgmt_cp_user_confirm_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001937
1938 BT_DBG("");
1939
1940 if (len != sizeof(*cp))
1941 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_REPLY,
1942 MGMT_STATUS_INVALID_PARAMS);
1943
Johan Hedberg272d90d2012-02-09 15:26:12 +02001944 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1945 MGMT_OP_USER_CONFIRM_REPLY,
1946 HCI_OP_USER_CONFIRM_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001947}
1948
1949static int user_confirm_neg_reply(struct sock *sk, u16 index, void *data,
1950 u16 len)
1951{
Johan Hedbergc9c26592011-12-15 00:47:41 +02001952 struct mgmt_cp_user_confirm_neg_reply *cp = data;
Brian Gix0df4c182011-11-16 13:53:13 -08001953
1954 BT_DBG("");
1955
1956 if (len != sizeof(*cp))
1957 return cmd_status(sk, index, MGMT_OP_USER_CONFIRM_NEG_REPLY,
1958 MGMT_STATUS_INVALID_PARAMS);
1959
Johan Hedberg272d90d2012-02-09 15:26:12 +02001960 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1961 MGMT_OP_USER_CONFIRM_NEG_REPLY,
1962 HCI_OP_USER_CONFIRM_NEG_REPLY, 0);
Brian Gix0df4c182011-11-16 13:53:13 -08001963}
1964
Brian Gix604086b2011-11-23 08:28:33 -08001965static int user_passkey_reply(struct sock *sk, u16 index, void *data, u16 len)
1966{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001967 struct mgmt_cp_user_passkey_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001968
1969 BT_DBG("");
1970
1971 if (len != sizeof(*cp))
1972 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_REPLY,
1973 EINVAL);
1974
Johan Hedberg272d90d2012-02-09 15:26:12 +02001975 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1976 MGMT_OP_USER_PASSKEY_REPLY,
1977 HCI_OP_USER_PASSKEY_REPLY,
1978 cp->passkey);
Brian Gix604086b2011-11-23 08:28:33 -08001979}
1980
1981static int user_passkey_neg_reply(struct sock *sk, u16 index, void *data,
1982 u16 len)
1983{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001984 struct mgmt_cp_user_passkey_neg_reply *cp = data;
Brian Gix604086b2011-11-23 08:28:33 -08001985
1986 BT_DBG("");
1987
1988 if (len != sizeof(*cp))
1989 return cmd_status(sk, index, MGMT_OP_USER_PASSKEY_NEG_REPLY,
1990 EINVAL);
1991
Johan Hedberg272d90d2012-02-09 15:26:12 +02001992 return user_pairing_resp(sk, index, &cp->addr.bdaddr, cp->addr.type,
1993 MGMT_OP_USER_PASSKEY_NEG_REPLY,
1994 HCI_OP_USER_PASSKEY_NEG_REPLY, 0);
Brian Gix604086b2011-11-23 08:28:33 -08001995}
1996
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03001997static int set_local_name(struct sock *sk, u16 index, void *data,
Johan Hedbergb312b1612011-03-16 14:29:37 +02001998 u16 len)
1999{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002000 struct mgmt_cp_set_local_name *mgmt_cp = data;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002001 struct hci_cp_write_local_name hci_cp;
2002 struct hci_dev *hdev;
2003 struct pending_cmd *cmd;
2004 int err;
2005
2006 BT_DBG("");
2007
2008 if (len != sizeof(*mgmt_cp))
Johan Hedbergca69b792011-11-11 18:10:00 +02002009 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2010 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002011
2012 hdev = hci_dev_get(index);
2013 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002014 return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME,
2015 MGMT_STATUS_INVALID_PARAMS);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002016
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002017 hci_dev_lock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002018
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002019 cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data,
2020 len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002021 if (!cmd) {
2022 err = -ENOMEM;
2023 goto failed;
2024 }
2025
2026 memcpy(hci_cp.name, mgmt_cp->name, sizeof(hci_cp.name));
2027 err = hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(hci_cp),
2028 &hci_cp);
2029 if (err < 0)
2030 mgmt_pending_remove(cmd);
2031
2032failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002033 hci_dev_unlock(hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002034 hci_dev_put(hdev);
2035
2036 return err;
2037}
2038
Szymon Jancc35938b2011-03-22 13:12:21 +01002039static int read_local_oob_data(struct sock *sk, u16 index)
2040{
2041 struct hci_dev *hdev;
2042 struct pending_cmd *cmd;
2043 int err;
2044
2045 BT_DBG("hci%u", index);
2046
2047 hdev = hci_dev_get(index);
2048 if (!hdev)
2049 return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002050 MGMT_STATUS_INVALID_PARAMS);
Szymon Jancc35938b2011-03-22 13:12:21 +01002051
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002052 hci_dev_lock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002053
2054 if (!test_bit(HCI_UP, &hdev->flags)) {
2055 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002056 MGMT_STATUS_NOT_POWERED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002057 goto unlock;
2058 }
2059
2060 if (!(hdev->features[6] & LMP_SIMPLE_PAIR)) {
2061 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002062 MGMT_STATUS_NOT_SUPPORTED);
Szymon Jancc35938b2011-03-22 13:12:21 +01002063 goto unlock;
2064 }
2065
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002066 if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002067 err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
2068 MGMT_STATUS_BUSY);
Szymon Jancc35938b2011-03-22 13:12:21 +01002069 goto unlock;
2070 }
2071
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002072 cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0);
Szymon Jancc35938b2011-03-22 13:12:21 +01002073 if (!cmd) {
2074 err = -ENOMEM;
2075 goto unlock;
2076 }
2077
2078 err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL);
2079 if (err < 0)
2080 mgmt_pending_remove(cmd);
2081
2082unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002083 hci_dev_unlock(hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01002084 hci_dev_put(hdev);
2085
2086 return err;
2087}
2088
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002089static int add_remote_oob_data(struct sock *sk, u16 index, void *data,
2090 u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002091{
2092 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002093 struct mgmt_cp_add_remote_oob_data *cp = data;
Szymon Janc2763eda2011-03-22 13:12:22 +01002094 int err;
2095
2096 BT_DBG("hci%u ", index);
2097
2098 if (len != sizeof(*cp))
2099 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002100 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002101
2102 hdev = hci_dev_get(index);
2103 if (!hdev)
2104 return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002105 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002106
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002107 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002108
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002109 err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, cp->hash,
Szymon Janc2763eda2011-03-22 13:12:22 +01002110 cp->randomizer);
2111 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02002112 err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
2113 MGMT_STATUS_FAILED);
Szymon Janc2763eda2011-03-22 13:12:22 +01002114 else
2115 err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
2116 0);
2117
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002118 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002119 hci_dev_put(hdev);
2120
2121 return err;
2122}
2123
2124static int remove_remote_oob_data(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002125 void *data, u16 len)
Szymon Janc2763eda2011-03-22 13:12:22 +01002126{
2127 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002128 struct mgmt_cp_remove_remote_oob_data *cp = data;
Szymon Janc2763eda2011-03-22 13:12:22 +01002129 int err;
2130
2131 BT_DBG("hci%u ", index);
2132
2133 if (len != sizeof(*cp))
2134 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002135 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002136
2137 hdev = hci_dev_get(index);
2138 if (!hdev)
2139 return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002140 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002141
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002142 hci_dev_lock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002143
Johan Hedberg664ce4c2012-02-09 15:44:09 +02002144 err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
Szymon Janc2763eda2011-03-22 13:12:22 +01002145 if (err < 0)
2146 err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
Johan Hedbergca69b792011-11-11 18:10:00 +02002147 MGMT_STATUS_INVALID_PARAMS);
Szymon Janc2763eda2011-03-22 13:12:22 +01002148 else
2149 err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
2150 NULL, 0);
2151
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002152 hci_dev_unlock(hdev);
Szymon Janc2763eda2011-03-22 13:12:22 +01002153 hci_dev_put(hdev);
2154
2155 return err;
2156}
2157
Andre Guedes5e0452c2012-02-17 20:39:38 -03002158static int discovery(struct hci_dev *hdev)
2159{
2160 int err;
2161
2162 if (lmp_host_le_capable(hdev)) {
2163 if (lmp_bredr_capable(hdev)) {
2164 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2165 LE_SCAN_INT, LE_SCAN_WIN,
2166 LE_SCAN_TIMEOUT_BREDR_LE);
2167 } else {
2168 hdev->discovery.type = DISCOV_TYPE_LE;
2169 err = hci_le_scan(hdev, LE_SCAN_TYPE,
2170 LE_SCAN_INT, LE_SCAN_WIN,
2171 LE_SCAN_TIMEOUT_LE_ONLY);
2172 }
2173 } else {
2174 hdev->discovery.type = DISCOV_TYPE_BREDR;
2175 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
2176 }
2177
2178 return err;
2179}
2180
2181int mgmt_interleaved_discovery(struct hci_dev *hdev)
2182{
2183 int err;
2184
2185 BT_DBG("%s", hdev->name);
2186
2187 hci_dev_lock(hdev);
2188
2189 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR_LE);
2190 if (err < 0)
2191 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2192
2193 hci_dev_unlock(hdev);
2194
2195 return err;
2196}
2197
Johan Hedberg450dfda2011-11-12 11:58:22 +02002198static int start_discovery(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002199 void *data, u16 len)
Johan Hedberg14a53662011-04-27 10:29:56 -04002200{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002201 struct mgmt_cp_start_discovery *cp = data;
Johan Hedberg14a53662011-04-27 10:29:56 -04002202 struct pending_cmd *cmd;
2203 struct hci_dev *hdev;
2204 int err;
2205
2206 BT_DBG("hci%u", index);
2207
Johan Hedberg450dfda2011-11-12 11:58:22 +02002208 if (len != sizeof(*cp))
2209 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2210 MGMT_STATUS_INVALID_PARAMS);
2211
Johan Hedberg14a53662011-04-27 10:29:56 -04002212 hdev = hci_dev_get(index);
2213 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002214 return cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2215 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002216
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002217 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002218
Johan Hedbergbd2d1332011-11-07 23:13:37 +02002219 if (!test_bit(HCI_UP, &hdev->flags)) {
Johan Hedbergca69b792011-11-11 18:10:00 +02002220 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2221 MGMT_STATUS_NOT_POWERED);
Johan Hedbergbd2d1332011-11-07 23:13:37 +02002222 goto failed;
2223 }
2224
Johan Hedbergff9ef572012-01-04 14:23:45 +02002225 if (hdev->discovery.state != DISCOVERY_STOPPED) {
2226 err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY,
2227 MGMT_STATUS_BUSY);
2228 goto failed;
2229 }
2230
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002231 cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002232 if (!cmd) {
2233 err = -ENOMEM;
2234 goto failed;
2235 }
2236
Andre Guedes4aab14e2012-02-17 20:39:36 -03002237 hdev->discovery.type = cp->type;
2238
2239 switch (hdev->discovery.type) {
Andre Guedesf39799f2012-02-17 20:39:35 -03002240 case DISCOV_TYPE_BREDR:
Andre Guedes3fd24152012-02-03 17:48:01 -03002241 err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR);
Andre Guedesf39799f2012-02-17 20:39:35 -03002242 break;
2243
2244 case DISCOV_TYPE_LE:
Andre Guedes3fd24152012-02-03 17:48:01 -03002245 err = hci_le_scan(hdev, LE_SCAN_TYPE, LE_SCAN_INT,
2246 LE_SCAN_WIN, LE_SCAN_TIMEOUT_LE_ONLY);
Andre Guedesf39799f2012-02-17 20:39:35 -03002247 break;
2248
Andre Guedes5e0452c2012-02-17 20:39:38 -03002249 case DISCOV_TYPE_INTERLEAVED:
2250 err = discovery(hdev);
2251 break;
2252
Andre Guedesf39799f2012-02-17 20:39:35 -03002253 default:
Andre Guedes3fd24152012-02-03 17:48:01 -03002254 err = -EINVAL;
Andre Guedesf39799f2012-02-17 20:39:35 -03002255 }
Andre Guedes3fd24152012-02-03 17:48:01 -03002256
Johan Hedberg14a53662011-04-27 10:29:56 -04002257 if (err < 0)
2258 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002259 else
2260 hci_discovery_set_state(hdev, DISCOVERY_STARTING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002261
2262failed:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002263 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002264 hci_dev_put(hdev);
2265
2266 return err;
2267}
2268
2269static int stop_discovery(struct sock *sk, u16 index)
2270{
2271 struct hci_dev *hdev;
2272 struct pending_cmd *cmd;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002273 struct hci_cp_remote_name_req_cancel cp;
2274 struct inquiry_entry *e;
Johan Hedberg14a53662011-04-27 10:29:56 -04002275 int err;
2276
2277 BT_DBG("hci%u", index);
2278
2279 hdev = hci_dev_get(index);
2280 if (!hdev)
Johan Hedbergca69b792011-11-11 18:10:00 +02002281 return cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2282 MGMT_STATUS_INVALID_PARAMS);
Johan Hedberg14a53662011-04-27 10:29:56 -04002283
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002284 hci_dev_lock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002285
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002286 if (!hci_discovery_active(hdev)) {
Johan Hedbergff9ef572012-01-04 14:23:45 +02002287 err = cmd_status(sk, index, MGMT_OP_STOP_DISCOVERY,
2288 MGMT_STATUS_REJECTED);
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002289 goto unlock;
Johan Hedbergff9ef572012-01-04 14:23:45 +02002290 }
2291
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002292 cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
Johan Hedberg14a53662011-04-27 10:29:56 -04002293 if (!cmd) {
2294 err = -ENOMEM;
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002295 goto unlock;
Johan Hedberg14a53662011-04-27 10:29:56 -04002296 }
2297
Andre Guedes343f9352012-02-17 20:39:37 -03002298 if (hdev->discovery.state == DISCOVERY_FINDING) {
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002299 err = hci_cancel_inquiry(hdev);
2300 if (err < 0)
2301 mgmt_pending_remove(cmd);
2302 else
2303 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
2304 goto unlock;
2305 }
2306
2307 e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING);
2308 if (!e) {
2309 mgmt_pending_remove(cmd);
2310 err = cmd_complete(sk, index, MGMT_OP_STOP_DISCOVERY, NULL, 0);
2311 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
2312 goto unlock;
2313 }
2314
2315 bacpy(&cp.bdaddr, &e->data.bdaddr);
2316 err = hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ_CANCEL,
2317 sizeof(cp), &cp);
Johan Hedberg14a53662011-04-27 10:29:56 -04002318 if (err < 0)
2319 mgmt_pending_remove(cmd);
Johan Hedbergff9ef572012-01-04 14:23:45 +02002320 else
2321 hci_discovery_set_state(hdev, DISCOVERY_STOPPING);
Johan Hedberg14a53662011-04-27 10:29:56 -04002322
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002323unlock:
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002324 hci_dev_unlock(hdev);
Johan Hedberg14a53662011-04-27 10:29:56 -04002325 hci_dev_put(hdev);
2326
2327 return err;
2328}
2329
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002330static int confirm_name(struct sock *sk, u16 index, void *data, u16 len)
Johan Hedberg561aafb2012-01-04 13:31:59 +02002331{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002332 struct mgmt_cp_confirm_name *cp = data;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002333 struct inquiry_entry *e;
2334 struct hci_dev *hdev;
2335 int err;
2336
2337 BT_DBG("hci%u", index);
2338
2339 if (len != sizeof(*cp))
2340 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2341 MGMT_STATUS_INVALID_PARAMS);
2342
2343 hdev = hci_dev_get(index);
2344 if (!hdev)
2345 return cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2346 MGMT_STATUS_INVALID_PARAMS);
2347
2348 hci_dev_lock(hdev);
2349
Johan Hedberg30dc78e2012-01-04 15:44:20 +02002350 if (!hci_discovery_active(hdev)) {
2351 err = cmd_status(sk, index, MGMT_OP_CONFIRM_NAME,
2352 MGMT_STATUS_FAILED);
2353 goto failed;
2354 }
2355
Johan Hedberga198e7b2012-02-17 14:27:06 +02002356 e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002357 if (!e) {
2358 err = cmd_status (sk, index, MGMT_OP_CONFIRM_NAME,
2359 MGMT_STATUS_INVALID_PARAMS);
2360 goto failed;
2361 }
2362
2363 if (cp->name_known) {
2364 e->name_state = NAME_KNOWN;
2365 list_del(&e->list);
2366 } else {
2367 e->name_state = NAME_NEEDED;
Johan Hedberga3d4e202012-01-09 00:53:02 +02002368 hci_inquiry_cache_update_resolve(hdev, e);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002369 }
2370
2371 err = 0;
2372
2373failed:
2374 hci_dev_unlock(hdev);
2375
2376 return err;
2377}
2378
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002379static int block_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002380{
2381 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002382 struct mgmt_cp_block_device *cp = data;
Antti Julku7fbec222011-06-15 12:01:15 +03002383 int err;
2384
2385 BT_DBG("hci%u", index);
2386
Antti Julku7fbec222011-06-15 12:01:15 +03002387 if (len != sizeof(*cp))
2388 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002389 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002390
2391 hdev = hci_dev_get(index);
2392 if (!hdev)
2393 return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002394 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002395
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002396 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002397
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002398 err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002399 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02002400 err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
2401 MGMT_STATUS_FAILED);
Antti Julku7fbec222011-06-15 12:01:15 +03002402 else
2403 err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
2404 NULL, 0);
Antti Julku5e762442011-08-25 16:48:02 +03002405
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002406 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002407 hci_dev_put(hdev);
2408
2409 return err;
2410}
2411
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002412static int unblock_device(struct sock *sk, u16 index, void *data, u16 len)
Antti Julku7fbec222011-06-15 12:01:15 +03002413{
2414 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002415 struct mgmt_cp_unblock_device *cp = data;
Antti Julku7fbec222011-06-15 12:01:15 +03002416 int err;
2417
2418 BT_DBG("hci%u", index);
2419
Antti Julku7fbec222011-06-15 12:01:15 +03002420 if (len != sizeof(*cp))
2421 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002422 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002423
2424 hdev = hci_dev_get(index);
2425 if (!hdev)
2426 return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002427 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002428
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002429 hci_dev_lock(hdev);
Antti Julku5e762442011-08-25 16:48:02 +03002430
Johan Hedberg88c1fe42012-02-09 15:56:11 +02002431 err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type);
Antti Julku7fbec222011-06-15 12:01:15 +03002432
2433 if (err < 0)
Johan Hedbergca69b792011-11-11 18:10:00 +02002434 err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2435 MGMT_STATUS_INVALID_PARAMS);
Antti Julku7fbec222011-06-15 12:01:15 +03002436 else
2437 err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
2438 NULL, 0);
Antti Julku5e762442011-08-25 16:48:02 +03002439
Gustavo F. Padovan09fd0de2011-06-17 13:03:21 -03002440 hci_dev_unlock(hdev);
Antti Julku7fbec222011-06-15 12:01:15 +03002441 hci_dev_put(hdev);
2442
2443 return err;
2444}
2445
Antti Julkuf6422ec2011-06-22 13:11:56 +03002446static int set_fast_connectable(struct sock *sk, u16 index,
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002447 void *data, u16 len)
Antti Julkuf6422ec2011-06-22 13:11:56 +03002448{
2449 struct hci_dev *hdev;
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002450 struct mgmt_mode *cp = data;
Antti Julkuf6422ec2011-06-22 13:11:56 +03002451 struct hci_cp_write_page_scan_activity acp;
2452 u8 type;
2453 int err;
2454
2455 BT_DBG("hci%u", index);
2456
2457 if (len != sizeof(*cp))
2458 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002459 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002460
2461 hdev = hci_dev_get(index);
2462 if (!hdev)
2463 return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002464 MGMT_STATUS_INVALID_PARAMS);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002465
2466 hci_dev_lock(hdev);
2467
Johan Hedbergf7c68692011-12-15 00:47:36 +02002468 if (cp->val) {
Antti Julkuf6422ec2011-06-22 13:11:56 +03002469 type = PAGE_SCAN_TYPE_INTERLACED;
2470 acp.interval = 0x0024; /* 22.5 msec page scan interval */
2471 } else {
2472 type = PAGE_SCAN_TYPE_STANDARD; /* default */
2473 acp.interval = 0x0800; /* default 1.28 sec page scan */
2474 }
2475
2476 acp.window = 0x0012; /* default 11.25 msec page scan window */
2477
2478 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY,
2479 sizeof(acp), &acp);
2480 if (err < 0) {
2481 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002482 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002483 goto done;
2484 }
2485
2486 err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type);
2487 if (err < 0) {
2488 err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
Johan Hedbergca69b792011-11-11 18:10:00 +02002489 MGMT_STATUS_FAILED);
Antti Julkuf6422ec2011-06-22 13:11:56 +03002490 goto done;
2491 }
2492
2493 err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE,
2494 NULL, 0);
2495done:
2496 hci_dev_unlock(hdev);
2497 hci_dev_put(hdev);
2498
2499 return err;
2500}
2501
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002502static int load_long_term_keys(struct sock *sk, u16 index,
2503 void *cp_data, u16 len)
2504{
2505 struct hci_dev *hdev;
2506 struct mgmt_cp_load_long_term_keys *cp = cp_data;
2507 u16 key_count, expected_len;
2508 int i;
2509
2510 if (len < sizeof(*cp))
2511 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2512 EINVAL);
2513
2514 key_count = get_unaligned_le16(&cp->key_count);
2515
2516 expected_len = sizeof(*cp) + key_count *
2517 sizeof(struct mgmt_ltk_info);
2518 if (expected_len != len) {
2519 BT_ERR("load_keys: expected %u bytes, got %u bytes",
2520 len, expected_len);
2521 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2522 EINVAL);
2523 }
2524
2525 hdev = hci_dev_get(index);
2526 if (!hdev)
2527 return cmd_status(sk, index, MGMT_OP_LOAD_LONG_TERM_KEYS,
2528 ENODEV);
2529
2530 BT_DBG("hci%u key_count %u", index, key_count);
2531
2532 hci_dev_lock(hdev);
2533
2534 hci_smp_ltks_clear(hdev);
2535
2536 for (i = 0; i < key_count; i++) {
2537 struct mgmt_ltk_info *key = &cp->keys[i];
2538 u8 type;
2539
2540 if (key->master)
2541 type = HCI_SMP_LTK;
2542 else
2543 type = HCI_SMP_LTK_SLAVE;
2544
2545 hci_add_ltk(hdev, &key->addr.bdaddr, key->addr.type,
2546 type, 0, key->authenticated, key->val,
2547 key->enc_size, key->ediv, key->rand);
2548 }
2549
2550 hci_dev_unlock(hdev);
2551 hci_dev_put(hdev);
2552
2553 return 0;
2554}
2555
Johan Hedberg03811012010-12-08 00:21:06 +02002556int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
2557{
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002558 void *buf;
2559 u8 *cp;
Johan Hedberg03811012010-12-08 00:21:06 +02002560 struct mgmt_hdr *hdr;
Szymon Janc4e51eae2011-02-25 19:05:48 +01002561 u16 opcode, index, len;
Johan Hedberg03811012010-12-08 00:21:06 +02002562 int err;
2563
2564 BT_DBG("got %zu bytes", msglen);
2565
2566 if (msglen < sizeof(*hdr))
2567 return -EINVAL;
2568
Gustavo F. Padovane63a15e2011-04-04 18:56:53 -03002569 buf = kmalloc(msglen, GFP_KERNEL);
Johan Hedberg03811012010-12-08 00:21:06 +02002570 if (!buf)
2571 return -ENOMEM;
2572
2573 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
2574 err = -EFAULT;
2575 goto done;
2576 }
2577
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002578 hdr = buf;
Johan Hedberg03811012010-12-08 00:21:06 +02002579 opcode = get_unaligned_le16(&hdr->opcode);
Szymon Janc4e51eae2011-02-25 19:05:48 +01002580 index = get_unaligned_le16(&hdr->index);
Johan Hedberg03811012010-12-08 00:21:06 +02002581 len = get_unaligned_le16(&hdr->len);
2582
2583 if (len != msglen - sizeof(*hdr)) {
2584 err = -EINVAL;
2585 goto done;
2586 }
2587
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002588 cp = buf + sizeof(*hdr);
2589
Johan Hedberg03811012010-12-08 00:21:06 +02002590 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +02002591 case MGMT_OP_READ_VERSION:
2592 err = read_version(sk);
2593 break;
Johan Hedberge70bb2e2012-02-13 16:59:33 +02002594 case MGMT_OP_READ_COMMANDS:
2595 err = read_commands(sk);
2596 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +02002597 case MGMT_OP_READ_INDEX_LIST:
2598 err = read_index_list(sk);
2599 break;
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002600 case MGMT_OP_READ_INFO:
Szymon Janc4e51eae2011-02-25 19:05:48 +01002601 err = read_controller_info(sk, index);
Johan Hedbergf7b64e62010-12-13 21:07:06 +02002602 break;
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002603 case MGMT_OP_SET_POWERED:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002604 err = set_powered(sk, index, cp, len);
Johan Hedbergeec8d2b2010-12-16 10:17:38 +02002605 break;
Johan Hedberg73f22f62010-12-29 16:00:25 +02002606 case MGMT_OP_SET_DISCOVERABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002607 err = set_discoverable(sk, index, cp, len);
Johan Hedberg73f22f62010-12-29 16:00:25 +02002608 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002609 case MGMT_OP_SET_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002610 err = set_connectable(sk, index, cp, len);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002611 break;
Johan Hedbergf7c68692011-12-15 00:47:36 +02002612 case MGMT_OP_SET_FAST_CONNECTABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002613 err = set_fast_connectable(sk, index, cp, len);
Johan Hedbergf7c68692011-12-15 00:47:36 +02002614 break;
Johan Hedbergc542a062011-01-26 13:11:03 +02002615 case MGMT_OP_SET_PAIRABLE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002616 err = set_pairable(sk, index, cp, len);
Johan Hedbergc542a062011-01-26 13:11:03 +02002617 break;
Johan Hedberg33ef95e2012-02-16 23:56:27 +02002618 case MGMT_OP_SET_LINK_SECURITY:
2619 err = set_link_security(sk, index, cp, len);
2620 break;
Johan Hedberged2c4ee2012-02-17 00:56:28 +02002621 case MGMT_OP_SET_SSP:
2622 err = set_ssp(sk, index, cp, len);
2623 break;
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002624 case MGMT_OP_ADD_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002625 err = add_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002626 break;
2627 case MGMT_OP_REMOVE_UUID:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002628 err = remove_uuid(sk, index, cp, len);
Johan Hedberg2aeb9a12011-01-04 12:08:51 +02002629 break;
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002630 case MGMT_OP_SET_DEV_CLASS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002631 err = set_dev_class(sk, index, cp, len);
Johan Hedberg1aff6f02011-01-13 21:56:52 +02002632 break;
Johan Hedberg86742e12011-11-07 23:13:38 +02002633 case MGMT_OP_LOAD_LINK_KEYS:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002634 err = load_link_keys(sk, index, cp, len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02002635 break;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002636 case MGMT_OP_DISCONNECT:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002637 err = disconnect(sk, index, cp, len);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002638 break;
Johan Hedberg2784eb42011-01-21 13:56:35 +02002639 case MGMT_OP_GET_CONNECTIONS:
Szymon Janc8ce62842011-03-01 16:55:32 +01002640 err = get_connections(sk, index);
Johan Hedberg2784eb42011-01-21 13:56:35 +02002641 break;
Johan Hedberg980e1a52011-01-22 06:10:07 +02002642 case MGMT_OP_PIN_CODE_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002643 err = pin_code_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002644 break;
2645 case MGMT_OP_PIN_CODE_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002646 err = pin_code_neg_reply(sk, index, cp, len);
Johan Hedberg980e1a52011-01-22 06:10:07 +02002647 break;
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002648 case MGMT_OP_SET_IO_CAPABILITY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002649 err = set_io_capability(sk, index, cp, len);
Johan Hedberg17fa4b92011-01-25 13:28:33 +02002650 break;
Johan Hedberge9a416b2011-02-19 12:05:56 -03002651 case MGMT_OP_PAIR_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002652 err = pair_device(sk, index, cp, len);
Johan Hedberge9a416b2011-02-19 12:05:56 -03002653 break;
Johan Hedberg28424702012-02-02 04:02:29 +02002654 case MGMT_OP_CANCEL_PAIR_DEVICE:
2655 err = cancel_pair_device(sk, index, buf + sizeof(*hdr), len);
2656 break;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002657 case MGMT_OP_UNPAIR_DEVICE:
2658 err = unpair_device(sk, index, cp, len);
2659 break;
Johan Hedberga5c29682011-02-19 12:05:57 -03002660 case MGMT_OP_USER_CONFIRM_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002661 err = user_confirm_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002662 break;
2663 case MGMT_OP_USER_CONFIRM_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002664 err = user_confirm_neg_reply(sk, index, cp, len);
Johan Hedberga5c29682011-02-19 12:05:57 -03002665 break;
Brian Gix604086b2011-11-23 08:28:33 -08002666 case MGMT_OP_USER_PASSKEY_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002667 err = user_passkey_reply(sk, index, cp, len);
Brian Gix604086b2011-11-23 08:28:33 -08002668 break;
2669 case MGMT_OP_USER_PASSKEY_NEG_REPLY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002670 err = user_passkey_neg_reply(sk, index, cp, len);
Johan Hedberg55ed8ca2011-01-17 14:41:05 +02002671 break;
Johan Hedbergb312b1612011-03-16 14:29:37 +02002672 case MGMT_OP_SET_LOCAL_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002673 err = set_local_name(sk, index, cp, len);
Johan Hedbergb312b1612011-03-16 14:29:37 +02002674 break;
Szymon Jancc35938b2011-03-22 13:12:21 +01002675 case MGMT_OP_READ_LOCAL_OOB_DATA:
2676 err = read_local_oob_data(sk, index);
2677 break;
Szymon Janc2763eda2011-03-22 13:12:22 +01002678 case MGMT_OP_ADD_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002679 err = add_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002680 break;
2681 case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002682 err = remove_remote_oob_data(sk, index, cp, len);
Szymon Janc2763eda2011-03-22 13:12:22 +01002683 break;
Johan Hedberg14a53662011-04-27 10:29:56 -04002684 case MGMT_OP_START_DISCOVERY:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002685 err = start_discovery(sk, index, cp, len);
Johan Hedberg14a53662011-04-27 10:29:56 -04002686 break;
2687 case MGMT_OP_STOP_DISCOVERY:
2688 err = stop_discovery(sk, index);
2689 break;
Johan Hedberg561aafb2012-01-04 13:31:59 +02002690 case MGMT_OP_CONFIRM_NAME:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002691 err = confirm_name(sk, index, cp, len);
Johan Hedberg561aafb2012-01-04 13:31:59 +02002692 break;
Antti Julku7fbec222011-06-15 12:01:15 +03002693 case MGMT_OP_BLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002694 err = block_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002695 break;
2696 case MGMT_OP_UNBLOCK_DEVICE:
Vinicius Costa Gomes650f7262012-02-02 21:07:59 -03002697 err = unblock_device(sk, index, cp, len);
Antti Julku7fbec222011-06-15 12:01:15 +03002698 break;
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002699 case MGMT_OP_LOAD_LONG_TERM_KEYS:
2700 err = load_long_term_keys(sk, index, cp, len);
2701 break;
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002702 default:
Johan Hedberg72a734e2010-12-30 00:38:22 +02002703 BT_DBG("Unknown op %u", opcode);
Johan Hedbergca69b792011-11-11 18:10:00 +02002704 err = cmd_status(sk, index, opcode,
2705 MGMT_STATUS_UNKNOWN_COMMAND);
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002706 break;
2707 }
Johan Hedberg72a734e2010-12-30 00:38:22 +02002708
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002709 if (err < 0)
Johan Hedberg72a734e2010-12-30 00:38:22 +02002710 goto done;
2711
Johan Hedberg9fbcbb42010-12-30 00:18:33 +02002712 err = msglen;
2713
2714done:
2715 kfree(buf);
2716 return err;
2717}
2718
Johan Hedbergb24752f2011-11-03 14:40:33 +02002719static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
2720{
2721 u8 *status = data;
2722
2723 cmd_status(cmd->sk, cmd->index, cmd->opcode, *status);
2724 mgmt_pending_remove(cmd);
2725}
2726
Johan Hedberg744cf192011-11-08 20:40:14 +02002727int mgmt_index_added(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002728{
Johan Hedberg744cf192011-11-08 20:40:14 +02002729 return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002730}
2731
Johan Hedberg744cf192011-11-08 20:40:14 +02002732int mgmt_index_removed(struct hci_dev *hdev)
Johan Hedberg03811012010-12-08 00:21:06 +02002733{
Johan Hedbergb24752f2011-11-03 14:40:33 +02002734 u8 status = ENODEV;
2735
Johan Hedberg744cf192011-11-08 20:40:14 +02002736 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002737
Johan Hedberg744cf192011-11-08 20:40:14 +02002738 return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002739}
2740
2741struct cmd_lookup {
Johan Hedberg03811012010-12-08 00:21:06 +02002742 struct sock *sk;
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002743 struct hci_dev *hdev;
Johan Hedberg03811012010-12-08 00:21:06 +02002744};
2745
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002746static void settings_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberg03811012010-12-08 00:21:06 +02002747{
Johan Hedberg03811012010-12-08 00:21:06 +02002748 struct cmd_lookup *match = data;
2749
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002750 send_settings_rsp(cmd->sk, cmd->opcode, match->hdev);
Johan Hedberg03811012010-12-08 00:21:06 +02002751
2752 list_del(&cmd->list);
2753
2754 if (match->sk == NULL) {
2755 match->sk = cmd->sk;
2756 sock_hold(match->sk);
2757 }
2758
2759 mgmt_pending_free(cmd);
2760}
2761
Johan Hedberg744cf192011-11-08 20:40:14 +02002762int mgmt_powered(struct hci_dev *hdev, u8 powered)
Johan Hedberg03811012010-12-08 00:21:06 +02002763{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002764 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002765 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002766 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002767
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002768 mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002769
Johan Hedbergb24752f2011-11-03 14:40:33 +02002770 if (!powered) {
2771 u8 status = ENETDOWN;
Johan Hedberg744cf192011-11-08 20:40:14 +02002772 mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
Johan Hedbergb24752f2011-11-03 14:40:33 +02002773 }
2774
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002775 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002776
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002777 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002778 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002779
2780 if (match.sk)
2781 sock_put(match.sk);
2782
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002783 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002784}
2785
Johan Hedberg744cf192011-11-08 20:40:14 +02002786int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable)
Johan Hedberg03811012010-12-08 00:21:06 +02002787{
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002788 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002789 __le32 ev;
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002790 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002791
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002792 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002793
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002794 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002795
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002796 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01002797 match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002798 if (match.sk)
2799 sock_put(match.sk);
2800
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002801 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002802}
2803
Johan Hedberg744cf192011-11-08 20:40:14 +02002804int mgmt_connectable(struct hci_dev *hdev, u8 connectable)
Johan Hedberg03811012010-12-08 00:21:06 +02002805{
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002806 __le32 ev;
Johan Hedberg76a7f3a2012-02-17 00:34:40 +02002807 struct cmd_lookup match = { NULL, hdev };
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002808 int err;
Johan Hedberg03811012010-12-08 00:21:06 +02002809
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002810 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp,
2811 &match);
Johan Hedberg03811012010-12-08 00:21:06 +02002812
Johan Hedberg69ab39e2011-12-15 00:47:35 +02002813 ev = cpu_to_le32(get_current_settings(hdev));
Johan Hedberg03811012010-12-08 00:21:06 +02002814
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002815 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
Johan Hedberg03811012010-12-08 00:21:06 +02002816
2817 if (match.sk)
2818 sock_put(match.sk);
2819
Johan Hedberg7bb895d2012-02-17 01:20:00 +02002820 return err;
Johan Hedberg03811012010-12-08 00:21:06 +02002821}
2822
Johan Hedberg744cf192011-11-08 20:40:14 +02002823int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status)
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002824{
Johan Hedbergca69b792011-11-11 18:10:00 +02002825 u8 mgmt_err = mgmt_status(status);
2826
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002827 if (scan & SCAN_PAGE)
Johan Hedberg744cf192011-11-08 20:40:14 +02002828 mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002829 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002830
2831 if (scan & SCAN_INQUIRY)
Johan Hedberg744cf192011-11-08 20:40:14 +02002832 mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev,
Johan Hedbergca69b792011-11-11 18:10:00 +02002833 cmd_status_rsp, &mgmt_err);
Johan Hedberg2d7cee52011-11-07 22:16:03 +02002834
2835 return 0;
2836}
2837
Johan Hedberg744cf192011-11-08 20:40:14 +02002838int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
2839 u8 persistent)
Johan Hedberg03811012010-12-08 00:21:06 +02002840{
Johan Hedberg86742e12011-11-07 23:13:38 +02002841 struct mgmt_ev_new_link_key ev;
Johan Hedberg03811012010-12-08 00:21:06 +02002842
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002843 memset(&ev, 0, sizeof(ev));
Johan Hedberg03811012010-12-08 00:21:06 +02002844
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002845 ev.store_hint = persistent;
Johan Hedbergd753fdc2012-02-17 14:06:34 +02002846 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2847 ev.key.addr.type = MGMT_ADDR_BREDR;
Vinicius Costa Gomesa492cd52011-08-25 20:02:29 -03002848 ev.key.type = key->type;
2849 memcpy(ev.key.val, key->val, 16);
2850 ev.key.pin_len = key->pin_len;
Johan Hedberg03811012010-12-08 00:21:06 +02002851
Johan Hedberg744cf192011-11-08 20:40:14 +02002852 return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg03811012010-12-08 00:21:06 +02002853}
Johan Hedbergf7520542011-01-20 12:34:39 +02002854
Vinicius Costa Gomes346af672012-02-02 21:08:02 -03002855int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent)
2856{
2857 struct mgmt_ev_new_long_term_key ev;
2858
2859 memset(&ev, 0, sizeof(ev));
2860
2861 ev.store_hint = persistent;
2862 bacpy(&ev.key.addr.bdaddr, &key->bdaddr);
2863 ev.key.addr.type = key->bdaddr_type;
2864 ev.key.authenticated = key->authenticated;
2865 ev.key.enc_size = key->enc_size;
2866 ev.key.ediv = key->ediv;
2867
2868 if (key->type == HCI_SMP_LTK)
2869 ev.key.master = 1;
2870
2871 memcpy(ev.key.rand, key->rand, sizeof(key->rand));
2872 memcpy(ev.key.val, key->val, sizeof(key->val));
2873
2874 return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev,
2875 &ev, sizeof(ev), NULL);
2876}
2877
Johan Hedbergafc747a2012-01-15 18:11:07 +02002878int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedbergb644ba32012-01-17 21:48:47 +02002879 u8 addr_type, u8 *name, u8 name_len,
2880 u8 *dev_class)
Johan Hedbergf7520542011-01-20 12:34:39 +02002881{
Johan Hedbergb644ba32012-01-17 21:48:47 +02002882 char buf[512];
2883 struct mgmt_ev_device_connected *ev = (void *) buf;
2884 u16 eir_len = 0;
Johan Hedbergf7520542011-01-20 12:34:39 +02002885
Johan Hedbergb644ba32012-01-17 21:48:47 +02002886 bacpy(&ev->addr.bdaddr, bdaddr);
2887 ev->addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002888
Johan Hedbergb644ba32012-01-17 21:48:47 +02002889 if (name_len > 0)
2890 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
2891 name, name_len);
2892
2893 if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
2894 eir_len = eir_append_data(&ev->eir[eir_len], eir_len,
2895 EIR_CLASS_OF_DEV, dev_class, 3);
2896
2897 put_unaligned_le16(eir_len, &ev->eir_len);
2898
2899 return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf,
2900 sizeof(*ev) + eir_len, NULL);
Johan Hedbergf7520542011-01-20 12:34:39 +02002901}
2902
Johan Hedberg8962ee72011-01-20 12:40:27 +02002903static void disconnect_rsp(struct pending_cmd *cmd, void *data)
2904{
Szymon Jancc68fb7f2011-03-22 13:12:19 +01002905 struct mgmt_cp_disconnect *cp = cmd->param;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002906 struct sock **sk = data;
Johan Hedberga38528f2011-01-22 06:46:43 +02002907 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002908
Johan Hedberg88c3df12012-02-09 14:27:38 +02002909 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2910 rp.addr.type = cp->addr.type;
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002911 rp.status = 0;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002912
Szymon Janc4e51eae2011-02-25 19:05:48 +01002913 cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02002914
2915 *sk = cmd->sk;
2916 sock_hold(*sk);
2917
Johan Hedberga664b5b2011-02-19 12:06:02 -03002918 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002919}
2920
Johan Hedberg124f6e32012-02-09 13:50:12 +02002921static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
Johan Hedberga8a1d192011-11-10 15:54:38 +02002922{
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002923 struct hci_dev *hdev = data;
Johan Hedberg124f6e32012-02-09 13:50:12 +02002924 struct mgmt_cp_unpair_device *cp = cmd->param;
2925 struct mgmt_rp_unpair_device rp;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002926
2927 memset(&rp, 0, sizeof(rp));
Johan Hedberg124f6e32012-02-09 13:50:12 +02002928 bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
2929 rp.addr.type = cp->addr.type;
Johan Hedberga8a1d192011-11-10 15:54:38 +02002930
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002931 device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
2932
2933 cmd_complete(cmd->sk, cmd->index, cmd->opcode, &rp, sizeof(rp));
Johan Hedberga8a1d192011-11-10 15:54:38 +02002934
2935 mgmt_pending_remove(cmd);
2936}
2937
Johan Hedbergafc747a2012-01-15 18:11:07 +02002938int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
2939 u8 link_type, u8 addr_type)
Johan Hedbergf7520542011-01-20 12:34:39 +02002940{
Johan Hedberg4c659c32011-11-07 23:13:39 +02002941 struct mgmt_addr_info ev;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002942 struct sock *sk = NULL;
2943 int err;
2944
Johan Hedberg744cf192011-11-08 20:40:14 +02002945 mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
Johan Hedbergf7520542011-01-20 12:34:39 +02002946
Johan Hedbergf7520542011-01-20 12:34:39 +02002947 bacpy(&ev.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02002948 ev.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergf7520542011-01-20 12:34:39 +02002949
Johan Hedbergafc747a2012-01-15 18:11:07 +02002950 err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev),
2951 sk);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002952
2953 if (sk)
2954 sock_put(sk);
2955
Johan Hedberg124f6e32012-02-09 13:50:12 +02002956 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002957 hdev);
Johan Hedberga8a1d192011-11-10 15:54:38 +02002958
Johan Hedberg8962ee72011-01-20 12:40:27 +02002959 return err;
2960}
2961
Johan Hedberg88c3df12012-02-09 14:27:38 +02002962int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
2963 u8 link_type, u8 addr_type, u8 status)
Johan Hedberg8962ee72011-01-20 12:40:27 +02002964{
Johan Hedberg88c3df12012-02-09 14:27:38 +02002965 struct mgmt_rp_disconnect rp;
Johan Hedberg8962ee72011-01-20 12:40:27 +02002966 struct pending_cmd *cmd;
2967 int err;
2968
Johan Hedberg2e58ef32011-11-08 20:40:15 +02002969 cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002970 if (!cmd)
2971 return -ENOENT;
2972
Johan Hedberg88c3df12012-02-09 14:27:38 +02002973 bacpy(&rp.addr.bdaddr, bdaddr);
2974 rp.addr.type = link_to_mgmt(link_type, addr_type);
2975 rp.status = mgmt_status(status);
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002976
Johan Hedberg88c3df12012-02-09 14:27:38 +02002977 err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
Johan Hedberg37d9ef72011-11-10 15:54:39 +02002978 &rp, sizeof(rp));
Johan Hedberg8962ee72011-01-20 12:40:27 +02002979
Johan Hedberga664b5b2011-02-19 12:06:02 -03002980 mgmt_pending_remove(cmd);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002981
Johan Hedbergb1078ad2012-02-09 17:21:16 +02002982 mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
2983 hdev);
Johan Hedberg8962ee72011-01-20 12:40:27 +02002984 return err;
Johan Hedbergf7520542011-01-20 12:34:39 +02002985}
Johan Hedberg17d5c042011-01-22 06:09:08 +02002986
Johan Hedberg48264f02011-11-09 13:58:58 +02002987int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
2988 u8 addr_type, u8 status)
Johan Hedberg17d5c042011-01-22 06:09:08 +02002989{
2990 struct mgmt_ev_connect_failed ev;
2991
Johan Hedberg4c659c32011-11-07 23:13:39 +02002992 bacpy(&ev.addr.bdaddr, bdaddr);
Johan Hedberg48264f02011-11-09 13:58:58 +02002993 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02002994 ev.status = mgmt_status(status);
Johan Hedberg17d5c042011-01-22 06:09:08 +02002995
Johan Hedberg744cf192011-11-08 20:40:14 +02002996 return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg17d5c042011-01-22 06:09:08 +02002997}
Johan Hedberg980e1a52011-01-22 06:10:07 +02002998
Johan Hedberg744cf192011-11-08 20:40:14 +02002999int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003000{
3001 struct mgmt_ev_pin_code_request ev;
3002
Johan Hedbergd8457692012-02-17 14:24:57 +02003003 bacpy(&ev.addr.bdaddr, bdaddr);
3004 ev.addr.type = MGMT_ADDR_BREDR;
Waldemar Rymarkiewicza770bb52011-04-28 12:07:59 +02003005 ev.secure = secure;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003006
Johan Hedberg744cf192011-11-08 20:40:14 +02003007 return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003008 NULL);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003009}
3010
Johan Hedberg744cf192011-11-08 20:40:14 +02003011int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3012 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003013{
3014 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003015 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003016 int err;
3017
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003018 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003019 if (!cmd)
3020 return -ENOENT;
3021
Johan Hedbergd8457692012-02-17 14:24:57 +02003022 bacpy(&rp.addr.bdaddr, bdaddr);
3023 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergca69b792011-11-11 18:10:00 +02003024 rp.status = mgmt_status(status);
Johan Hedbergac56fb12011-02-19 12:05:59 -03003025
Johan Hedberg744cf192011-11-08 20:40:14 +02003026 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +01003027 sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003028
Johan Hedberga664b5b2011-02-19 12:06:02 -03003029 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003030
3031 return err;
3032}
3033
Johan Hedberg744cf192011-11-08 20:40:14 +02003034int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3035 u8 status)
Johan Hedberg980e1a52011-01-22 06:10:07 +02003036{
3037 struct pending_cmd *cmd;
Johan Hedbergac56fb12011-02-19 12:05:59 -03003038 struct mgmt_rp_pin_code_reply rp;
Johan Hedberg980e1a52011-01-22 06:10:07 +02003039 int err;
3040
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003041 cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003042 if (!cmd)
3043 return -ENOENT;
3044
Johan Hedbergd8457692012-02-17 14:24:57 +02003045 bacpy(&rp.addr.bdaddr, bdaddr);
3046 rp.addr.type = MGMT_ADDR_BREDR;
Johan Hedbergca69b792011-11-11 18:10:00 +02003047 rp.status = mgmt_status(status);
Johan Hedbergac56fb12011-02-19 12:05:59 -03003048
Johan Hedberg744cf192011-11-08 20:40:14 +02003049 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp,
Szymon Janc4e51eae2011-02-25 19:05:48 +01003050 sizeof(rp));
Johan Hedberg980e1a52011-01-22 06:10:07 +02003051
Johan Hedberga664b5b2011-02-19 12:06:02 -03003052 mgmt_pending_remove(cmd);
Johan Hedberg980e1a52011-01-22 06:10:07 +02003053
3054 return err;
3055}
Johan Hedberga5c29682011-02-19 12:05:57 -03003056
Johan Hedberg744cf192011-11-08 20:40:14 +02003057int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003058 u8 link_type, u8 addr_type, __le32 value,
3059 u8 confirm_hint)
Johan Hedberga5c29682011-02-19 12:05:57 -03003060{
3061 struct mgmt_ev_user_confirm_request ev;
3062
Johan Hedberg744cf192011-11-08 20:40:14 +02003063 BT_DBG("%s", hdev->name);
Johan Hedberga5c29682011-02-19 12:05:57 -03003064
Johan Hedberg272d90d2012-02-09 15:26:12 +02003065 bacpy(&ev.addr.bdaddr, bdaddr);
3066 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedberg55bc1a32011-04-28 11:28:56 -07003067 ev.confirm_hint = confirm_hint;
Johan Hedberga5c29682011-02-19 12:05:57 -03003068 put_unaligned_le32(value, &ev.value);
3069
Johan Hedberg744cf192011-11-08 20:40:14 +02003070 return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev),
Szymon Janc4e51eae2011-02-25 19:05:48 +01003071 NULL);
Johan Hedberga5c29682011-02-19 12:05:57 -03003072}
3073
Johan Hedberg272d90d2012-02-09 15:26:12 +02003074int mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr,
3075 u8 link_type, u8 addr_type)
Brian Gix604086b2011-11-23 08:28:33 -08003076{
3077 struct mgmt_ev_user_passkey_request ev;
3078
3079 BT_DBG("%s", hdev->name);
3080
Johan Hedberg272d90d2012-02-09 15:26:12 +02003081 bacpy(&ev.addr.bdaddr, bdaddr);
3082 ev.addr.type = link_to_mgmt(link_type, addr_type);
Brian Gix604086b2011-11-23 08:28:33 -08003083
3084 return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev),
3085 NULL);
3086}
3087
Brian Gix0df4c182011-11-16 13:53:13 -08003088static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003089 u8 link_type, u8 addr_type, u8 status,
3090 u8 opcode)
Johan Hedberga5c29682011-02-19 12:05:57 -03003091{
3092 struct pending_cmd *cmd;
3093 struct mgmt_rp_user_confirm_reply rp;
3094 int err;
3095
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003096 cmd = mgmt_pending_find(opcode, hdev);
Johan Hedberga5c29682011-02-19 12:05:57 -03003097 if (!cmd)
3098 return -ENOENT;
3099
Johan Hedberg272d90d2012-02-09 15:26:12 +02003100 bacpy(&rp.addr.bdaddr, bdaddr);
3101 rp.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003102 rp.status = mgmt_status(status);
Johan Hedberg744cf192011-11-08 20:40:14 +02003103 err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp));
Johan Hedberga5c29682011-02-19 12:05:57 -03003104
Johan Hedberga664b5b2011-02-19 12:06:02 -03003105 mgmt_pending_remove(cmd);
Johan Hedberga5c29682011-02-19 12:05:57 -03003106
3107 return err;
3108}
3109
Johan Hedberg744cf192011-11-08 20:40:14 +02003110int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003111 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003112{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003113 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3114 status, MGMT_OP_USER_CONFIRM_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003115}
3116
Johan Hedberg272d90d2012-02-09 15:26:12 +02003117int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3118 u8 link_type, u8 addr_type, u8 status)
Johan Hedberga5c29682011-02-19 12:05:57 -03003119{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003120 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3121 status, MGMT_OP_USER_CONFIRM_NEG_REPLY);
Johan Hedberga5c29682011-02-19 12:05:57 -03003122}
Johan Hedberg2a611692011-02-19 12:06:00 -03003123
Brian Gix604086b2011-11-23 08:28:33 -08003124int mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
Johan Hedberg272d90d2012-02-09 15:26:12 +02003125 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003126{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003127 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3128 status, MGMT_OP_USER_PASSKEY_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003129}
3130
Johan Hedberg272d90d2012-02-09 15:26:12 +02003131int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
3132 u8 link_type, u8 addr_type, u8 status)
Brian Gix604086b2011-11-23 08:28:33 -08003133{
Johan Hedberg272d90d2012-02-09 15:26:12 +02003134 return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type,
3135 status, MGMT_OP_USER_PASSKEY_NEG_REPLY);
Brian Gix604086b2011-11-23 08:28:33 -08003136}
3137
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003138int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3139 u8 addr_type, u8 status)
Johan Hedberg2a611692011-02-19 12:06:00 -03003140{
3141 struct mgmt_ev_auth_failed ev;
3142
Johan Hedbergbab73cb2012-02-09 16:07:29 +02003143 bacpy(&ev.addr.bdaddr, bdaddr);
3144 ev.addr.type = link_to_mgmt(link_type, addr_type);
Johan Hedbergca69b792011-11-11 18:10:00 +02003145 ev.status = mgmt_status(status);
Johan Hedberg2a611692011-02-19 12:06:00 -03003146
Johan Hedberg744cf192011-11-08 20:40:14 +02003147 return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL);
Johan Hedberg2a611692011-02-19 12:06:00 -03003148}
Johan Hedbergb312b1612011-03-16 14:29:37 +02003149
Johan Hedberg33ef95e2012-02-16 23:56:27 +02003150int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status)
3151{
3152 struct cmd_lookup match = { NULL, hdev };
3153 __le32 ev;
3154 int err;
3155
3156 if (status) {
3157 u8 mgmt_err = mgmt_status(status);
3158 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev,
3159 cmd_status_rsp, &mgmt_err);
3160 return 0;
3161 }
3162
3163 mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp,
3164 &match);
3165
3166 ev = cpu_to_le32(get_current_settings(hdev));
3167 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3168
3169 if (match.sk)
3170 sock_put(match.sk);
3171
3172 return err;
3173}
3174
Johan Hedberged2c4ee2012-02-17 00:56:28 +02003175int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 status)
3176{
3177 struct cmd_lookup match = { NULL, hdev };
3178 __le32 ev;
3179 int err;
3180
3181 if (status) {
3182 u8 mgmt_err = mgmt_status(status);
3183 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev,
3184 cmd_status_rsp, &mgmt_err);
3185 return 0;
3186 }
3187
3188 mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match);
3189
3190 ev = cpu_to_le32(get_current_settings(hdev));
3191 err = mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), match.sk);
3192
3193 if (match.sk)
3194 sock_put(match.sk);
3195
3196 return err;
3197}
3198
Johan Hedberg744cf192011-11-08 20:40:14 +02003199int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
Johan Hedbergb312b1612011-03-16 14:29:37 +02003200{
3201 struct pending_cmd *cmd;
3202 struct mgmt_cp_set_local_name ev;
3203 int err;
3204
3205 memset(&ev, 0, sizeof(ev));
3206 memcpy(ev.name, name, HCI_MAX_NAME_LENGTH);
3207
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003208 cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev);
Johan Hedbergb312b1612011-03-16 14:29:37 +02003209 if (!cmd)
3210 goto send_event;
3211
3212 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003213 err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME,
Johan Hedbergca69b792011-11-11 18:10:00 +02003214 mgmt_status(status));
Johan Hedbergb312b1612011-03-16 14:29:37 +02003215 goto failed;
3216 }
3217
Johan Hedberg744cf192011-11-08 20:40:14 +02003218 update_eir(hdev);
Johan Hedberg80a1e1d2011-03-28 14:07:23 +03003219
Johan Hedberg744cf192011-11-08 20:40:14 +02003220 err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev,
Johan Hedbergb312b1612011-03-16 14:29:37 +02003221 sizeof(ev));
3222 if (err < 0)
3223 goto failed;
3224
3225send_event:
Johan Hedberg744cf192011-11-08 20:40:14 +02003226 err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev),
Johan Hedbergb312b1612011-03-16 14:29:37 +02003227 cmd ? cmd->sk : NULL);
3228
3229failed:
3230 if (cmd)
3231 mgmt_pending_remove(cmd);
3232 return err;
3233}
Szymon Jancc35938b2011-03-22 13:12:21 +01003234
Johan Hedberg744cf192011-11-08 20:40:14 +02003235int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash,
3236 u8 *randomizer, u8 status)
Szymon Jancc35938b2011-03-22 13:12:21 +01003237{
3238 struct pending_cmd *cmd;
3239 int err;
3240
Johan Hedberg744cf192011-11-08 20:40:14 +02003241 BT_DBG("%s status %u", hdev->name, status);
Szymon Jancc35938b2011-03-22 13:12:21 +01003242
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003243 cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev);
Szymon Jancc35938b2011-03-22 13:12:21 +01003244 if (!cmd)
3245 return -ENOENT;
3246
3247 if (status) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003248 err = cmd_status(cmd->sk, hdev->id,
Johan Hedbergca69b792011-11-11 18:10:00 +02003249 MGMT_OP_READ_LOCAL_OOB_DATA,
3250 mgmt_status(status));
Szymon Jancc35938b2011-03-22 13:12:21 +01003251 } else {
3252 struct mgmt_rp_read_local_oob_data rp;
3253
3254 memcpy(rp.hash, hash, sizeof(rp.hash));
3255 memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer));
3256
Johan Hedberg744cf192011-11-08 20:40:14 +02003257 err = cmd_complete(cmd->sk, hdev->id,
3258 MGMT_OP_READ_LOCAL_OOB_DATA,
3259 &rp, sizeof(rp));
Szymon Jancc35938b2011-03-22 13:12:21 +01003260 }
3261
3262 mgmt_pending_remove(cmd);
3263
3264 return err;
3265}
Johan Hedberge17acd42011-03-30 23:57:16 +03003266
Johan Hedberg48264f02011-11-09 13:58:58 +02003267int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
Johan Hedberg561aafb2012-01-04 13:31:59 +02003268 u8 addr_type, u8 *dev_class, s8 rssi,
Johan Hedberge319d2e2012-01-15 19:51:59 +02003269 u8 cfm_name, u8 *eir, u16 eir_len)
Johan Hedberge17acd42011-03-30 23:57:16 +03003270{
Johan Hedberge319d2e2012-01-15 19:51:59 +02003271 char buf[512];
3272 struct mgmt_ev_device_found *ev = (void *) buf;
Johan Hedberg1dc06092012-01-15 21:01:23 +02003273 size_t ev_size;
Johan Hedberge17acd42011-03-30 23:57:16 +03003274
Johan Hedberg1dc06092012-01-15 21:01:23 +02003275 /* Leave 5 bytes for a potential CoD field */
3276 if (sizeof(*ev) + eir_len + 5 > sizeof(buf))
Andre Guedes7d262f82012-01-10 18:20:49 -03003277 return -EINVAL;
3278
Johan Hedberg1dc06092012-01-15 21:01:23 +02003279 memset(buf, 0, sizeof(buf));
3280
Johan Hedberge319d2e2012-01-15 19:51:59 +02003281 bacpy(&ev->addr.bdaddr, bdaddr);
3282 ev->addr.type = link_to_mgmt(link_type, addr_type);
3283 ev->rssi = rssi;
3284 ev->confirm_name = cfm_name;
Johan Hedberge17acd42011-03-30 23:57:16 +03003285
Johan Hedberg1dc06092012-01-15 21:01:23 +02003286 if (eir_len > 0)
Johan Hedberge319d2e2012-01-15 19:51:59 +02003287 memcpy(ev->eir, eir, eir_len);
Johan Hedberge17acd42011-03-30 23:57:16 +03003288
Johan Hedberg1dc06092012-01-15 21:01:23 +02003289 if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
3290 eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
3291 dev_class, 3);
3292
3293 put_unaligned_le16(eir_len, &ev->eir_len);
3294
3295 ev_size = sizeof(*ev) + eir_len;
Andre Guedesf8523592011-09-09 18:56:26 -03003296
Johan Hedberge319d2e2012-01-15 19:51:59 +02003297 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
Johan Hedberge17acd42011-03-30 23:57:16 +03003298}
Johan Hedberga88a9652011-03-30 13:18:12 +03003299
Johan Hedbergb644ba32012-01-17 21:48:47 +02003300int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
3301 u8 addr_type, s8 rssi, u8 *name, u8 name_len)
Johan Hedberga88a9652011-03-30 13:18:12 +03003302{
Johan Hedbergb644ba32012-01-17 21:48:47 +02003303 struct mgmt_ev_device_found *ev;
3304 char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2];
3305 u16 eir_len;
Johan Hedberga88a9652011-03-30 13:18:12 +03003306
Johan Hedbergb644ba32012-01-17 21:48:47 +02003307 ev = (struct mgmt_ev_device_found *) buf;
Johan Hedberga88a9652011-03-30 13:18:12 +03003308
Johan Hedbergb644ba32012-01-17 21:48:47 +02003309 memset(buf, 0, sizeof(buf));
Johan Hedberga88a9652011-03-30 13:18:12 +03003310
Johan Hedbergb644ba32012-01-17 21:48:47 +02003311 bacpy(&ev->addr.bdaddr, bdaddr);
3312 ev->addr.type = link_to_mgmt(link_type, addr_type);
3313 ev->rssi = rssi;
3314
3315 eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name,
3316 name_len);
3317
3318 put_unaligned_le16(eir_len, &ev->eir_len);
3319
Johan Hedberg053c7e02012-02-04 00:06:00 +02003320 return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
3321 sizeof(*ev) + eir_len, NULL);
Johan Hedberga88a9652011-03-30 13:18:12 +03003322}
Johan Hedberg314b2382011-04-27 10:29:57 -04003323
Andre Guedes7a135102011-11-09 17:14:25 -03003324int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
Johan Hedberg164a6e72011-11-01 17:06:44 +02003325{
3326 struct pending_cmd *cmd;
3327 int err;
3328
Andre Guedes203159d2012-02-13 15:41:01 -03003329 hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
3330
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003331 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003332 if (!cmd)
3333 return -ENOENT;
3334
Johan Hedbergca69b792011-11-11 18:10:00 +02003335 err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
Johan Hedberg164a6e72011-11-01 17:06:44 +02003336 mgmt_pending_remove(cmd);
3337
3338 return err;
3339}
3340
Andre Guedese6d465c2011-11-09 17:14:26 -03003341int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
3342{
3343 struct pending_cmd *cmd;
3344 int err;
3345
3346 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
3347 if (!cmd)
3348 return -ENOENT;
3349
Andre Guedese75a8b0c2012-01-02 16:50:53 -03003350 err = cmd_status(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status));
Johan Hedberg03811012010-12-08 00:21:06 +02003351 mgmt_pending_remove(cmd);
3352
3353 return err;
3354}
Johan Hedberg314b2382011-04-27 10:29:57 -04003355
Johan Hedberg744cf192011-11-08 20:40:14 +02003356int mgmt_discovering(struct hci_dev *hdev, u8 discovering)
Johan Hedberg314b2382011-04-27 10:29:57 -04003357{
Johan Hedberg164a6e72011-11-01 17:06:44 +02003358 struct pending_cmd *cmd;
3359
Andre Guedes343fb142011-11-22 17:14:19 -03003360 BT_DBG("%s discovering %u", hdev->name, discovering);
3361
Johan Hedberg164a6e72011-11-01 17:06:44 +02003362 if (discovering)
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003363 cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003364 else
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003365 cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003366
3367 if (cmd != NULL) {
Johan Hedberg744cf192011-11-08 20:40:14 +02003368 cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0);
Johan Hedberg164a6e72011-11-01 17:06:44 +02003369 mgmt_pending_remove(cmd);
3370 }
3371
Johan Hedberg744cf192011-11-08 20:40:14 +02003372 return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering,
Johan Hedberg314b2382011-04-27 10:29:57 -04003373 sizeof(discovering), NULL);
3374}
Antti Julku5e762442011-08-25 16:48:02 +03003375
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003376int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003377{
3378 struct pending_cmd *cmd;
3379 struct mgmt_ev_device_blocked ev;
3380
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003381 cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003382
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003383 bacpy(&ev.addr.bdaddr, bdaddr);
3384 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003385
Johan Hedberg744cf192011-11-08 20:40:14 +02003386 return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev),
3387 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003388}
3389
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003390int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
Antti Julku5e762442011-08-25 16:48:02 +03003391{
3392 struct pending_cmd *cmd;
3393 struct mgmt_ev_device_unblocked ev;
3394
Johan Hedberg2e58ef32011-11-08 20:40:15 +02003395 cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev);
Antti Julku5e762442011-08-25 16:48:02 +03003396
Johan Hedberg88c1fe42012-02-09 15:56:11 +02003397 bacpy(&ev.addr.bdaddr, bdaddr);
3398 ev.addr.type = type;
Antti Julku5e762442011-08-25 16:48:02 +03003399
Johan Hedberg744cf192011-11-08 20:40:14 +02003400 return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev),
3401 cmd ? cmd->sk : NULL);
Antti Julku5e762442011-08-25 16:48:02 +03003402}