]>
Commit | Line | Data |
---|---|---|
145373cb MC |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Copyright (C) 2020 Google Corporation | |
4 | */ | |
5 | ||
6 | #include <net/bluetooth/bluetooth.h> | |
7 | #include <net/bluetooth/hci_core.h> | |
a2a4dedf | 8 | #include <net/bluetooth/mgmt.h> |
145373cb | 9 | |
a2a4dedf AP |
10 | #include "hci_request.h" |
11 | #include "mgmt_util.h" | |
145373cb MC |
12 | #include "msft.h" |
13 | ||
a2a4dedf AP |
14 | #define MSFT_RSSI_THRESHOLD_VALUE_MIN -127 |
15 | #define MSFT_RSSI_THRESHOLD_VALUE_MAX 20 | |
16 | #define MSFT_RSSI_LOW_TIMEOUT_MAX 0x3C | |
17 | ||
145373cb MC |
18 | #define MSFT_OP_READ_SUPPORTED_FEATURES 0x00 |
19 | struct msft_cp_read_supported_features { | |
20 | __u8 sub_opcode; | |
21 | } __packed; | |
b08eadd2 | 22 | |
145373cb MC |
23 | struct msft_rp_read_supported_features { |
24 | __u8 status; | |
25 | __u8 sub_opcode; | |
26 | __le64 features; | |
27 | __u8 evt_prefix_len; | |
b08eadd2 | 28 | __u8 evt_prefix[]; |
145373cb MC |
29 | } __packed; |
30 | ||
a2a4dedf AP |
31 | #define MSFT_OP_LE_MONITOR_ADVERTISEMENT 0x03 |
32 | #define MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN 0x01 | |
33 | struct msft_le_monitor_advertisement_pattern { | |
34 | __u8 length; | |
35 | __u8 data_type; | |
36 | __u8 start_byte; | |
07d85dbe | 37 | __u8 pattern[]; |
a2a4dedf AP |
38 | }; |
39 | ||
40 | struct msft_le_monitor_advertisement_pattern_data { | |
41 | __u8 count; | |
07d85dbe | 42 | __u8 data[]; |
a2a4dedf AP |
43 | }; |
44 | ||
45 | struct msft_cp_le_monitor_advertisement { | |
46 | __u8 sub_opcode; | |
47 | __s8 rssi_high; | |
48 | __s8 rssi_low; | |
49 | __u8 rssi_low_interval; | |
50 | __u8 rssi_sampling_period; | |
51 | __u8 cond_type; | |
07d85dbe | 52 | __u8 data[]; |
a2a4dedf AP |
53 | } __packed; |
54 | ||
55 | struct msft_rp_le_monitor_advertisement { | |
56 | __u8 status; | |
57 | __u8 sub_opcode; | |
58 | __u8 handle; | |
59 | } __packed; | |
60 | ||
66bd095a AP |
61 | #define MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT 0x04 |
62 | struct msft_cp_le_cancel_monitor_advertisement { | |
63 | __u8 sub_opcode; | |
64 | __u8 handle; | |
65 | } __packed; | |
66 | ||
67 | struct msft_rp_le_cancel_monitor_advertisement { | |
68 | __u8 status; | |
69 | __u8 sub_opcode; | |
70 | } __packed; | |
71 | ||
394566bf AP |
72 | #define MSFT_OP_LE_SET_ADVERTISEMENT_FILTER_ENABLE 0x05 |
73 | struct msft_cp_le_set_advertisement_filter_enable { | |
74 | __u8 sub_opcode; | |
75 | __u8 enable; | |
76 | } __packed; | |
77 | ||
78 | struct msft_rp_le_set_advertisement_filter_enable { | |
79 | __u8 status; | |
80 | __u8 sub_opcode; | |
81 | } __packed; | |
82 | ||
a2a4dedf AP |
83 | struct msft_monitor_advertisement_handle_data { |
84 | __u8 msft_handle; | |
85 | __u16 mgmt_handle; | |
86 | struct list_head list; | |
87 | }; | |
88 | ||
145373cb MC |
89 | struct msft_data { |
90 | __u64 features; | |
91 | __u8 evt_prefix_len; | |
92 | __u8 *evt_prefix; | |
a2a4dedf AP |
93 | struct list_head handle_map; |
94 | __u16 pending_add_handle; | |
66bd095a | 95 | __u16 pending_remove_handle; |
4a37682c | 96 | __u8 reregistering; |
394566bf | 97 | __u8 filter_enabled; |
145373cb MC |
98 | }; |
99 | ||
4a37682c AP |
100 | static int __msft_add_monitor_pattern(struct hci_dev *hdev, |
101 | struct adv_monitor *monitor); | |
102 | ||
a2a4dedf AP |
103 | bool msft_monitor_supported(struct hci_dev *hdev) |
104 | { | |
105 | return !!(msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR); | |
106 | } | |
107 | ||
145373cb MC |
108 | static bool read_supported_features(struct hci_dev *hdev, |
109 | struct msft_data *msft) | |
110 | { | |
111 | struct msft_cp_read_supported_features cp; | |
112 | struct msft_rp_read_supported_features *rp; | |
113 | struct sk_buff *skb; | |
114 | ||
115 | cp.sub_opcode = MSFT_OP_READ_SUPPORTED_FEATURES; | |
116 | ||
117 | skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp, | |
118 | HCI_CMD_TIMEOUT); | |
119 | if (IS_ERR(skb)) { | |
120 | bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)", | |
121 | PTR_ERR(skb)); | |
122 | return false; | |
123 | } | |
124 | ||
125 | if (skb->len < sizeof(*rp)) { | |
126 | bt_dev_err(hdev, "MSFT supported features length mismatch"); | |
127 | goto failed; | |
128 | } | |
129 | ||
130 | rp = (struct msft_rp_read_supported_features *)skb->data; | |
131 | ||
132 | if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES) | |
133 | goto failed; | |
134 | ||
135 | if (rp->evt_prefix_len > 0) { | |
136 | msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len, | |
137 | GFP_KERNEL); | |
138 | if (!msft->evt_prefix) | |
139 | goto failed; | |
140 | } | |
141 | ||
142 | msft->evt_prefix_len = rp->evt_prefix_len; | |
143 | msft->features = __le64_to_cpu(rp->features); | |
144 | ||
a61d6718 MH |
145 | if (msft->features & MSFT_FEATURE_MASK_CURVE_VALIDITY) |
146 | hdev->msft_curve_validity = true; | |
147 | ||
145373cb MC |
148 | kfree_skb(skb); |
149 | return true; | |
150 | ||
151 | failed: | |
152 | kfree_skb(skb); | |
153 | return false; | |
154 | } | |
155 | ||
4a37682c AP |
156 | /* This function requires the caller holds hdev->lock */ |
157 | static void reregister_monitor_on_restart(struct hci_dev *hdev, int handle) | |
158 | { | |
159 | struct adv_monitor *monitor; | |
160 | struct msft_data *msft = hdev->msft_data; | |
161 | int err; | |
162 | ||
163 | while (1) { | |
164 | monitor = idr_get_next(&hdev->adv_monitors_idr, &handle); | |
165 | if (!monitor) { | |
166 | /* All monitors have been reregistered */ | |
167 | msft->reregistering = false; | |
168 | hci_update_background_scan(hdev); | |
169 | return; | |
170 | } | |
171 | ||
172 | msft->pending_add_handle = (u16)handle; | |
173 | err = __msft_add_monitor_pattern(hdev, monitor); | |
174 | ||
175 | /* If success, we return and wait for monitor added callback */ | |
176 | if (!err) | |
177 | return; | |
178 | ||
179 | /* Otherwise remove the monitor and keep registering */ | |
180 | hci_free_adv_monitor(hdev, monitor); | |
181 | handle++; | |
182 | } | |
183 | } | |
184 | ||
145373cb MC |
185 | void msft_do_open(struct hci_dev *hdev) |
186 | { | |
187 | struct msft_data *msft; | |
188 | ||
189 | if (hdev->msft_opcode == HCI_OP_NOP) | |
190 | return; | |
191 | ||
192 | bt_dev_dbg(hdev, "Initialize MSFT extension"); | |
193 | ||
194 | msft = kzalloc(sizeof(*msft), GFP_KERNEL); | |
195 | if (!msft) | |
196 | return; | |
197 | ||
198 | if (!read_supported_features(hdev, msft)) { | |
199 | kfree(msft); | |
200 | return; | |
201 | } | |
202 | ||
a2a4dedf | 203 | INIT_LIST_HEAD(&msft->handle_map); |
145373cb | 204 | hdev->msft_data = msft; |
4a37682c AP |
205 | |
206 | if (msft_monitor_supported(hdev)) { | |
207 | msft->reregistering = true; | |
394566bf | 208 | msft_set_filter_enable(hdev, true); |
4a37682c AP |
209 | reregister_monitor_on_restart(hdev, 0); |
210 | } | |
145373cb MC |
211 | } |
212 | ||
213 | void msft_do_close(struct hci_dev *hdev) | |
214 | { | |
215 | struct msft_data *msft = hdev->msft_data; | |
a2a4dedf | 216 | struct msft_monitor_advertisement_handle_data *handle_data, *tmp; |
4a37682c | 217 | struct adv_monitor *monitor; |
145373cb MC |
218 | |
219 | if (!msft) | |
220 | return; | |
221 | ||
222 | bt_dev_dbg(hdev, "Cleanup of MSFT extension"); | |
223 | ||
224 | hdev->msft_data = NULL; | |
225 | ||
a2a4dedf | 226 | list_for_each_entry_safe(handle_data, tmp, &msft->handle_map, list) { |
4a37682c AP |
227 | monitor = idr_find(&hdev->adv_monitors_idr, |
228 | handle_data->mgmt_handle); | |
229 | ||
230 | if (monitor && monitor->state == ADV_MONITOR_STATE_OFFLOADED) | |
231 | monitor->state = ADV_MONITOR_STATE_REGISTERED; | |
232 | ||
a2a4dedf AP |
233 | list_del(&handle_data->list); |
234 | kfree(handle_data); | |
235 | } | |
236 | ||
145373cb MC |
237 | kfree(msft->evt_prefix); |
238 | kfree(msft); | |
239 | } | |
240 | ||
241 | void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb) | |
242 | { | |
243 | struct msft_data *msft = hdev->msft_data; | |
244 | u8 event; | |
245 | ||
246 | if (!msft) | |
247 | return; | |
248 | ||
249 | /* When the extension has defined an event prefix, check that it | |
250 | * matches, and otherwise just return. | |
251 | */ | |
252 | if (msft->evt_prefix_len > 0) { | |
253 | if (skb->len < msft->evt_prefix_len) | |
254 | return; | |
255 | ||
256 | if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len)) | |
257 | return; | |
258 | ||
259 | skb_pull(skb, msft->evt_prefix_len); | |
260 | } | |
261 | ||
262 | /* Every event starts at least with an event code and the rest of | |
263 | * the data is variable and depends on the event code. | |
264 | */ | |
265 | if (skb->len < 1) | |
266 | return; | |
267 | ||
268 | event = *skb->data; | |
269 | skb_pull(skb, 1); | |
270 | ||
271 | bt_dev_dbg(hdev, "MSFT vendor event %u", event); | |
272 | } | |
e5e1e7fd MC |
273 | |
274 | __u64 msft_get_features(struct hci_dev *hdev) | |
275 | { | |
276 | struct msft_data *msft = hdev->msft_data; | |
277 | ||
a2a4dedf AP |
278 | return msft ? msft->features : 0; |
279 | } | |
280 | ||
66bd095a AP |
281 | /* is_mgmt = true matches the handle exposed to userspace via mgmt. |
282 | * is_mgmt = false matches the handle used by the msft controller. | |
283 | * This function requires the caller holds hdev->lock | |
284 | */ | |
285 | static struct msft_monitor_advertisement_handle_data *msft_find_handle_data | |
286 | (struct hci_dev *hdev, u16 handle, bool is_mgmt) | |
287 | { | |
288 | struct msft_monitor_advertisement_handle_data *entry; | |
289 | struct msft_data *msft = hdev->msft_data; | |
290 | ||
291 | list_for_each_entry(entry, &msft->handle_map, list) { | |
292 | if (is_mgmt && entry->mgmt_handle == handle) | |
293 | return entry; | |
294 | if (!is_mgmt && entry->msft_handle == handle) | |
295 | return entry; | |
296 | } | |
297 | ||
298 | return NULL; | |
299 | } | |
300 | ||
a2a4dedf AP |
301 | static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev, |
302 | u8 status, u16 opcode, | |
303 | struct sk_buff *skb) | |
304 | { | |
305 | struct msft_rp_le_monitor_advertisement *rp; | |
306 | struct adv_monitor *monitor; | |
307 | struct msft_monitor_advertisement_handle_data *handle_data; | |
308 | struct msft_data *msft = hdev->msft_data; | |
309 | ||
310 | hci_dev_lock(hdev); | |
311 | ||
312 | monitor = idr_find(&hdev->adv_monitors_idr, msft->pending_add_handle); | |
313 | if (!monitor) { | |
496bdeee | 314 | bt_dev_err(hdev, "msft add advmon: monitor %u is not found!", |
a2a4dedf AP |
315 | msft->pending_add_handle); |
316 | status = HCI_ERROR_UNSPECIFIED; | |
317 | goto unlock; | |
318 | } | |
319 | ||
320 | if (status) | |
321 | goto unlock; | |
322 | ||
323 | rp = (struct msft_rp_le_monitor_advertisement *)skb->data; | |
324 | if (skb->len < sizeof(*rp)) { | |
325 | status = HCI_ERROR_UNSPECIFIED; | |
326 | goto unlock; | |
327 | } | |
328 | ||
329 | handle_data = kmalloc(sizeof(*handle_data), GFP_KERNEL); | |
330 | if (!handle_data) { | |
331 | status = HCI_ERROR_UNSPECIFIED; | |
332 | goto unlock; | |
333 | } | |
334 | ||
335 | handle_data->mgmt_handle = monitor->handle; | |
336 | handle_data->msft_handle = rp->handle; | |
337 | INIT_LIST_HEAD(&handle_data->list); | |
338 | list_add(&handle_data->list, &msft->handle_map); | |
339 | ||
340 | monitor->state = ADV_MONITOR_STATE_OFFLOADED; | |
341 | ||
342 | unlock: | |
66bd095a AP |
343 | if (status && monitor) |
344 | hci_free_adv_monitor(hdev, monitor); | |
a2a4dedf | 345 | |
4a37682c AP |
346 | /* If in restart/reregister sequence, keep registering. */ |
347 | if (msft->reregistering) | |
348 | reregister_monitor_on_restart(hdev, | |
349 | msft->pending_add_handle + 1); | |
350 | ||
a2a4dedf AP |
351 | hci_dev_unlock(hdev); |
352 | ||
4a37682c AP |
353 | if (!msft->reregistering) |
354 | hci_add_adv_patterns_monitor_complete(hdev, status); | |
a2a4dedf AP |
355 | } |
356 | ||
66bd095a AP |
357 | static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev, |
358 | u8 status, u16 opcode, | |
359 | struct sk_buff *skb) | |
360 | { | |
361 | struct msft_cp_le_cancel_monitor_advertisement *cp; | |
362 | struct msft_rp_le_cancel_monitor_advertisement *rp; | |
363 | struct adv_monitor *monitor; | |
364 | struct msft_monitor_advertisement_handle_data *handle_data; | |
365 | struct msft_data *msft = hdev->msft_data; | |
366 | int err; | |
367 | bool pending; | |
368 | ||
369 | if (status) | |
370 | goto done; | |
371 | ||
372 | rp = (struct msft_rp_le_cancel_monitor_advertisement *)skb->data; | |
373 | if (skb->len < sizeof(*rp)) { | |
374 | status = HCI_ERROR_UNSPECIFIED; | |
375 | goto done; | |
376 | } | |
377 | ||
378 | hci_dev_lock(hdev); | |
379 | ||
380 | cp = hci_sent_cmd_data(hdev, hdev->msft_opcode); | |
381 | handle_data = msft_find_handle_data(hdev, cp->handle, false); | |
382 | ||
383 | if (handle_data) { | |
384 | monitor = idr_find(&hdev->adv_monitors_idr, | |
385 | handle_data->mgmt_handle); | |
386 | if (monitor) | |
387 | hci_free_adv_monitor(hdev, monitor); | |
388 | ||
389 | list_del(&handle_data->list); | |
390 | kfree(handle_data); | |
391 | } | |
392 | ||
393 | /* If remove all monitors is required, we need to continue the process | |
394 | * here because the earlier it was paused when waiting for the | |
395 | * response from controller. | |
396 | */ | |
397 | if (msft->pending_remove_handle == 0) { | |
398 | pending = hci_remove_all_adv_monitor(hdev, &err); | |
399 | if (pending) { | |
400 | hci_dev_unlock(hdev); | |
401 | return; | |
402 | } | |
403 | ||
404 | if (err) | |
405 | status = HCI_ERROR_UNSPECIFIED; | |
406 | } | |
407 | ||
408 | hci_dev_unlock(hdev); | |
409 | ||
410 | done: | |
411 | hci_remove_adv_monitor_complete(hdev, status); | |
412 | } | |
413 | ||
394566bf AP |
414 | static void msft_le_set_advertisement_filter_enable_cb(struct hci_dev *hdev, |
415 | u8 status, u16 opcode, | |
416 | struct sk_buff *skb) | |
417 | { | |
418 | struct msft_cp_le_set_advertisement_filter_enable *cp; | |
419 | struct msft_rp_le_set_advertisement_filter_enable *rp; | |
420 | struct msft_data *msft = hdev->msft_data; | |
421 | ||
422 | rp = (struct msft_rp_le_set_advertisement_filter_enable *)skb->data; | |
423 | if (skb->len < sizeof(*rp)) | |
424 | return; | |
425 | ||
426 | /* Error 0x0C would be returned if the filter enabled status is | |
427 | * already set to whatever we were trying to set. | |
428 | * Although the default state should be disabled, some controller set | |
429 | * the initial value to enabled. Because there is no way to know the | |
430 | * actual initial value before sending this command, here we also treat | |
431 | * error 0x0C as success. | |
432 | */ | |
433 | if (status != 0x00 && status != 0x0C) | |
434 | return; | |
435 | ||
436 | hci_dev_lock(hdev); | |
437 | ||
438 | cp = hci_sent_cmd_data(hdev, hdev->msft_opcode); | |
439 | msft->filter_enabled = cp->enable; | |
440 | ||
441 | if (status == 0x0C) | |
442 | bt_dev_warn(hdev, "MSFT filter_enable is already %s", | |
443 | cp->enable ? "on" : "off"); | |
444 | ||
445 | hci_dev_unlock(hdev); | |
446 | } | |
447 | ||
a2a4dedf AP |
448 | static bool msft_monitor_rssi_valid(struct adv_monitor *monitor) |
449 | { | |
450 | struct adv_rssi_thresholds *r = &monitor->rssi; | |
451 | ||
452 | if (r->high_threshold < MSFT_RSSI_THRESHOLD_VALUE_MIN || | |
453 | r->high_threshold > MSFT_RSSI_THRESHOLD_VALUE_MAX || | |
454 | r->low_threshold < MSFT_RSSI_THRESHOLD_VALUE_MIN || | |
455 | r->low_threshold > MSFT_RSSI_THRESHOLD_VALUE_MAX) | |
456 | return false; | |
457 | ||
458 | /* High_threshold_timeout is not supported, | |
459 | * once high_threshold is reached, events are immediately reported. | |
460 | */ | |
461 | if (r->high_threshold_timeout != 0) | |
462 | return false; | |
463 | ||
464 | if (r->low_threshold_timeout > MSFT_RSSI_LOW_TIMEOUT_MAX) | |
465 | return false; | |
466 | ||
467 | /* Sampling period from 0x00 to 0xFF are all allowed */ | |
468 | return true; | |
469 | } | |
470 | ||
471 | static bool msft_monitor_pattern_valid(struct adv_monitor *monitor) | |
472 | { | |
473 | return msft_monitor_rssi_valid(monitor); | |
474 | /* No additional check needed for pattern-based monitor */ | |
475 | } | |
476 | ||
477 | /* This function requires the caller holds hdev->lock */ | |
4a37682c AP |
478 | static int __msft_add_monitor_pattern(struct hci_dev *hdev, |
479 | struct adv_monitor *monitor) | |
a2a4dedf AP |
480 | { |
481 | struct msft_cp_le_monitor_advertisement *cp; | |
482 | struct msft_le_monitor_advertisement_pattern_data *pattern_data; | |
483 | struct msft_le_monitor_advertisement_pattern *pattern; | |
484 | struct adv_pattern *entry; | |
485 | struct hci_request req; | |
486 | struct msft_data *msft = hdev->msft_data; | |
487 | size_t total_size = sizeof(*cp) + sizeof(*pattern_data); | |
488 | ptrdiff_t offset = 0; | |
489 | u8 pattern_count = 0; | |
490 | int err = 0; | |
491 | ||
a2a4dedf AP |
492 | if (!msft_monitor_pattern_valid(monitor)) |
493 | return -EINVAL; | |
494 | ||
495 | list_for_each_entry(entry, &monitor->patterns, list) { | |
496 | pattern_count++; | |
497 | total_size += sizeof(*pattern) + entry->length; | |
498 | } | |
499 | ||
500 | cp = kmalloc(total_size, GFP_KERNEL); | |
501 | if (!cp) | |
502 | return -ENOMEM; | |
503 | ||
504 | cp->sub_opcode = MSFT_OP_LE_MONITOR_ADVERTISEMENT; | |
505 | cp->rssi_high = monitor->rssi.high_threshold; | |
506 | cp->rssi_low = monitor->rssi.low_threshold; | |
507 | cp->rssi_low_interval = (u8)monitor->rssi.low_threshold_timeout; | |
508 | cp->rssi_sampling_period = monitor->rssi.sampling_period; | |
509 | ||
510 | cp->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN; | |
511 | ||
512 | pattern_data = (void *)cp->data; | |
513 | pattern_data->count = pattern_count; | |
514 | ||
515 | list_for_each_entry(entry, &monitor->patterns, list) { | |
516 | pattern = (void *)(pattern_data->data + offset); | |
517 | /* the length also includes data_type and offset */ | |
518 | pattern->length = entry->length + 2; | |
519 | pattern->data_type = entry->ad_type; | |
520 | pattern->start_byte = entry->offset; | |
521 | memcpy(pattern->pattern, entry->value, entry->length); | |
522 | offset += sizeof(*pattern) + entry->length; | |
523 | } | |
524 | ||
525 | hci_req_init(&req, hdev); | |
526 | hci_req_add(&req, hdev->msft_opcode, total_size, cp); | |
527 | err = hci_req_run_skb(&req, msft_le_monitor_advertisement_cb); | |
528 | kfree(cp); | |
529 | ||
530 | if (!err) | |
531 | msft->pending_add_handle = monitor->handle; | |
532 | ||
533 | return err; | |
e5e1e7fd | 534 | } |
66bd095a | 535 | |
4a37682c AP |
536 | /* This function requires the caller holds hdev->lock */ |
537 | int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor) | |
538 | { | |
539 | struct msft_data *msft = hdev->msft_data; | |
540 | ||
541 | if (!msft) | |
542 | return -EOPNOTSUPP; | |
543 | ||
544 | if (msft->reregistering) | |
545 | return -EBUSY; | |
546 | ||
547 | return __msft_add_monitor_pattern(hdev, monitor); | |
548 | } | |
549 | ||
66bd095a AP |
550 | /* This function requires the caller holds hdev->lock */ |
551 | int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor, | |
552 | u16 handle) | |
553 | { | |
554 | struct msft_cp_le_cancel_monitor_advertisement cp; | |
555 | struct msft_monitor_advertisement_handle_data *handle_data; | |
556 | struct hci_request req; | |
557 | struct msft_data *msft = hdev->msft_data; | |
558 | int err = 0; | |
559 | ||
560 | if (!msft) | |
561 | return -EOPNOTSUPP; | |
562 | ||
4a37682c AP |
563 | if (msft->reregistering) |
564 | return -EBUSY; | |
565 | ||
66bd095a AP |
566 | handle_data = msft_find_handle_data(hdev, monitor->handle, true); |
567 | ||
568 | /* If no matched handle, just remove without telling controller */ | |
569 | if (!handle_data) | |
570 | return -ENOENT; | |
571 | ||
572 | cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT; | |
573 | cp.handle = handle_data->msft_handle; | |
574 | ||
575 | hci_req_init(&req, hdev); | |
576 | hci_req_add(&req, hdev->msft_opcode, sizeof(cp), &cp); | |
577 | err = hci_req_run_skb(&req, msft_le_cancel_monitor_advertisement_cb); | |
578 | ||
579 | if (!err) | |
580 | msft->pending_remove_handle = handle; | |
581 | ||
582 | return err; | |
583 | } | |
394566bf | 584 | |
bf6a4e30 | 585 | void msft_req_add_set_filter_enable(struct hci_request *req, bool enable) |
394566bf | 586 | { |
bf6a4e30 | 587 | struct hci_dev *hdev = req->hdev; |
394566bf | 588 | struct msft_cp_le_set_advertisement_filter_enable cp; |
bf6a4e30 HC |
589 | |
590 | cp.sub_opcode = MSFT_OP_LE_SET_ADVERTISEMENT_FILTER_ENABLE; | |
591 | cp.enable = enable; | |
592 | ||
593 | hci_req_add(req, hdev->msft_opcode, sizeof(cp), &cp); | |
594 | } | |
595 | ||
596 | int msft_set_filter_enable(struct hci_dev *hdev, bool enable) | |
597 | { | |
394566bf AP |
598 | struct hci_request req; |
599 | struct msft_data *msft = hdev->msft_data; | |
600 | int err; | |
601 | ||
602 | if (!msft) | |
603 | return -EOPNOTSUPP; | |
604 | ||
394566bf | 605 | hci_req_init(&req, hdev); |
bf6a4e30 | 606 | msft_req_add_set_filter_enable(&req, enable); |
394566bf AP |
607 | err = hci_req_run_skb(&req, msft_le_set_advertisement_filter_enable_cb); |
608 | ||
609 | return err; | |
610 | } | |
a61d6718 MH |
611 | |
612 | bool msft_curve_validity(struct hci_dev *hdev) | |
613 | { | |
614 | return hdev->msft_curve_validity; | |
615 | } |