]> Git Repo - linux.git/blob - net/bluetooth/msft.c
topology/sysfs: rework book and drawer topology ifdefery
[linux.git] / net / bluetooth / msft.c
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>
8 #include <net/bluetooth/mgmt.h>
9
10 #include "hci_request.h"
11 #include "mgmt_util.h"
12 #include "msft.h"
13
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
18 #define MSFT_OP_READ_SUPPORTED_FEATURES         0x00
19 struct msft_cp_read_supported_features {
20         __u8   sub_opcode;
21 } __packed;
22
23 struct msft_rp_read_supported_features {
24         __u8   status;
25         __u8   sub_opcode;
26         __le64 features;
27         __u8   evt_prefix_len;
28         __u8   evt_prefix[];
29 } __packed;
30
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;
37         __u8 pattern[];
38 };
39
40 struct msft_le_monitor_advertisement_pattern_data {
41         __u8 count;
42         __u8 data[];
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;
52         __u8 data[];
53 } __packed;
54
55 struct msft_rp_le_monitor_advertisement {
56         __u8 status;
57         __u8 sub_opcode;
58         __u8 handle;
59 } __packed;
60
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
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
83 struct msft_monitor_advertisement_handle_data {
84         __u8  msft_handle;
85         __u16 mgmt_handle;
86         struct list_head list;
87 };
88
89 struct msft_data {
90         __u64 features;
91         __u8  evt_prefix_len;
92         __u8  *evt_prefix;
93         struct list_head handle_map;
94         __u16 pending_add_handle;
95         __u16 pending_remove_handle;
96         __u8 reregistering;
97         __u8 suspending;
98         __u8 filter_enabled;
99 };
100
101 static int __msft_add_monitor_pattern(struct hci_dev *hdev,
102                                       struct adv_monitor *monitor);
103 static int __msft_remove_monitor(struct hci_dev *hdev,
104                                  struct adv_monitor *monitor, u16 handle);
105
106 bool msft_monitor_supported(struct hci_dev *hdev)
107 {
108         return !!(msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR);
109 }
110
111 static bool read_supported_features(struct hci_dev *hdev,
112                                     struct msft_data *msft)
113 {
114         struct msft_cp_read_supported_features cp;
115         struct msft_rp_read_supported_features *rp;
116         struct sk_buff *skb;
117
118         cp.sub_opcode = MSFT_OP_READ_SUPPORTED_FEATURES;
119
120         skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
121                              HCI_CMD_TIMEOUT);
122         if (IS_ERR(skb)) {
123                 bt_dev_err(hdev, "Failed to read MSFT supported features (%ld)",
124                            PTR_ERR(skb));
125                 return false;
126         }
127
128         if (skb->len < sizeof(*rp)) {
129                 bt_dev_err(hdev, "MSFT supported features length mismatch");
130                 goto failed;
131         }
132
133         rp = (struct msft_rp_read_supported_features *)skb->data;
134
135         if (rp->sub_opcode != MSFT_OP_READ_SUPPORTED_FEATURES)
136                 goto failed;
137
138         if (rp->evt_prefix_len > 0) {
139                 msft->evt_prefix = kmemdup(rp->evt_prefix, rp->evt_prefix_len,
140                                            GFP_KERNEL);
141                 if (!msft->evt_prefix)
142                         goto failed;
143         }
144
145         msft->evt_prefix_len = rp->evt_prefix_len;
146         msft->features = __le64_to_cpu(rp->features);
147
148         if (msft->features & MSFT_FEATURE_MASK_CURVE_VALIDITY)
149                 hdev->msft_curve_validity = true;
150
151         kfree_skb(skb);
152         return true;
153
154 failed:
155         kfree_skb(skb);
156         return false;
157 }
158
159 /* This function requires the caller holds hdev->lock */
160 static void reregister_monitor(struct hci_dev *hdev, int handle)
161 {
162         struct adv_monitor *monitor;
163         struct msft_data *msft = hdev->msft_data;
164         int err;
165
166         while (1) {
167                 monitor = idr_get_next(&hdev->adv_monitors_idr, &handle);
168                 if (!monitor) {
169                         /* All monitors have been reregistered */
170                         msft->reregistering = false;
171                         hci_update_background_scan(hdev);
172                         return;
173                 }
174
175                 msft->pending_add_handle = (u16)handle;
176                 err = __msft_add_monitor_pattern(hdev, monitor);
177
178                 /* If success, we return and wait for monitor added callback */
179                 if (!err)
180                         return;
181
182                 /* Otherwise remove the monitor and keep registering */
183                 hci_free_adv_monitor(hdev, monitor);
184                 handle++;
185         }
186 }
187
188 /* This function requires the caller holds hdev->lock */
189 static void remove_monitor_on_suspend(struct hci_dev *hdev, int handle)
190 {
191         struct adv_monitor *monitor;
192         struct msft_data *msft = hdev->msft_data;
193         int err;
194
195         while (1) {
196                 monitor = idr_get_next(&hdev->adv_monitors_idr, &handle);
197                 if (!monitor) {
198                         /* All monitors have been removed */
199                         msft->suspending = false;
200                         hci_update_background_scan(hdev);
201                         return;
202                 }
203
204                 msft->pending_remove_handle = (u16)handle;
205                 err = __msft_remove_monitor(hdev, monitor, handle);
206
207                 /* If success, return and wait for monitor removed callback */
208                 if (!err)
209                         return;
210
211                 /* Otherwise free the monitor and keep removing */
212                 hci_free_adv_monitor(hdev, monitor);
213                 handle++;
214         }
215 }
216
217 /* This function requires the caller holds hdev->lock */
218 void msft_suspend(struct hci_dev *hdev)
219 {
220         struct msft_data *msft = hdev->msft_data;
221
222         if (!msft)
223                 return;
224
225         if (msft_monitor_supported(hdev)) {
226                 msft->suspending = true;
227                 /* Quitely remove all monitors on suspend to avoid waking up
228                  * the system.
229                  */
230                 remove_monitor_on_suspend(hdev, 0);
231         }
232 }
233
234 /* This function requires the caller holds hdev->lock */
235 void msft_resume(struct hci_dev *hdev)
236 {
237         struct msft_data *msft = hdev->msft_data;
238
239         if (!msft)
240                 return;
241
242         if (msft_monitor_supported(hdev)) {
243                 msft->reregistering = true;
244                 /* Monitors are removed on suspend, so we need to add all
245                  * monitors on resume.
246                  */
247                 reregister_monitor(hdev, 0);
248         }
249 }
250
251 void msft_do_open(struct hci_dev *hdev)
252 {
253         struct msft_data *msft = hdev->msft_data;
254
255         if (hdev->msft_opcode == HCI_OP_NOP)
256                 return;
257
258         if (!msft) {
259                 bt_dev_err(hdev, "MSFT extension not registered");
260                 return;
261         }
262
263         bt_dev_dbg(hdev, "Initialize MSFT extension");
264
265         /* Reset existing MSFT data before re-reading */
266         kfree(msft->evt_prefix);
267         msft->evt_prefix = NULL;
268         msft->evt_prefix_len = 0;
269         msft->features = 0;
270
271         if (!read_supported_features(hdev, msft)) {
272                 hdev->msft_data = NULL;
273                 kfree(msft);
274                 return;
275         }
276
277         if (msft_monitor_supported(hdev)) {
278                 msft->reregistering = true;
279                 msft_set_filter_enable(hdev, true);
280                 /* Monitors get removed on power off, so we need to explicitly
281                  * tell the controller to re-monitor.
282                  */
283                 reregister_monitor(hdev, 0);
284         }
285 }
286
287 void msft_do_close(struct hci_dev *hdev)
288 {
289         struct msft_data *msft = hdev->msft_data;
290         struct msft_monitor_advertisement_handle_data *handle_data, *tmp;
291         struct adv_monitor *monitor;
292
293         if (!msft)
294                 return;
295
296         bt_dev_dbg(hdev, "Cleanup of MSFT extension");
297
298         /* The controller will silently remove all monitors on power off.
299          * Therefore, remove handle_data mapping and reset monitor state.
300          */
301         list_for_each_entry_safe(handle_data, tmp, &msft->handle_map, list) {
302                 monitor = idr_find(&hdev->adv_monitors_idr,
303                                    handle_data->mgmt_handle);
304
305                 if (monitor && monitor->state == ADV_MONITOR_STATE_OFFLOADED)
306                         monitor->state = ADV_MONITOR_STATE_REGISTERED;
307
308                 list_del(&handle_data->list);
309                 kfree(handle_data);
310         }
311 }
312
313 void msft_register(struct hci_dev *hdev)
314 {
315         struct msft_data *msft = NULL;
316
317         bt_dev_dbg(hdev, "Register MSFT extension");
318
319         msft = kzalloc(sizeof(*msft), GFP_KERNEL);
320         if (!msft) {
321                 bt_dev_err(hdev, "Failed to register MSFT extension");
322                 return;
323         }
324
325         INIT_LIST_HEAD(&msft->handle_map);
326         hdev->msft_data = msft;
327 }
328
329 void msft_unregister(struct hci_dev *hdev)
330 {
331         struct msft_data *msft = hdev->msft_data;
332
333         if (!msft)
334                 return;
335
336         bt_dev_dbg(hdev, "Unregister MSFT extension");
337
338         hdev->msft_data = NULL;
339
340         kfree(msft->evt_prefix);
341         kfree(msft);
342 }
343
344 void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb)
345 {
346         struct msft_data *msft = hdev->msft_data;
347         u8 event;
348
349         if (!msft)
350                 return;
351
352         /* When the extension has defined an event prefix, check that it
353          * matches, and otherwise just return.
354          */
355         if (msft->evt_prefix_len > 0) {
356                 if (skb->len < msft->evt_prefix_len)
357                         return;
358
359                 if (memcmp(skb->data, msft->evt_prefix, msft->evt_prefix_len))
360                         return;
361
362                 skb_pull(skb, msft->evt_prefix_len);
363         }
364
365         /* Every event starts at least with an event code and the rest of
366          * the data is variable and depends on the event code.
367          */
368         if (skb->len < 1)
369                 return;
370
371         event = *skb->data;
372         skb_pull(skb, 1);
373
374         bt_dev_dbg(hdev, "MSFT vendor event %u", event);
375 }
376
377 __u64 msft_get_features(struct hci_dev *hdev)
378 {
379         struct msft_data *msft = hdev->msft_data;
380
381         return msft ? msft->features : 0;
382 }
383
384 /* is_mgmt = true matches the handle exposed to userspace via mgmt.
385  * is_mgmt = false matches the handle used by the msft controller.
386  * This function requires the caller holds hdev->lock
387  */
388 static struct msft_monitor_advertisement_handle_data *msft_find_handle_data
389                                 (struct hci_dev *hdev, u16 handle, bool is_mgmt)
390 {
391         struct msft_monitor_advertisement_handle_data *entry;
392         struct msft_data *msft = hdev->msft_data;
393
394         list_for_each_entry(entry, &msft->handle_map, list) {
395                 if (is_mgmt && entry->mgmt_handle == handle)
396                         return entry;
397                 if (!is_mgmt && entry->msft_handle == handle)
398                         return entry;
399         }
400
401         return NULL;
402 }
403
404 static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,
405                                              u8 status, u16 opcode,
406                                              struct sk_buff *skb)
407 {
408         struct msft_rp_le_monitor_advertisement *rp;
409         struct adv_monitor *monitor;
410         struct msft_monitor_advertisement_handle_data *handle_data;
411         struct msft_data *msft = hdev->msft_data;
412
413         hci_dev_lock(hdev);
414
415         monitor = idr_find(&hdev->adv_monitors_idr, msft->pending_add_handle);
416         if (!monitor) {
417                 bt_dev_err(hdev, "msft add advmon: monitor %u is not found!",
418                            msft->pending_add_handle);
419                 status = HCI_ERROR_UNSPECIFIED;
420                 goto unlock;
421         }
422
423         if (status)
424                 goto unlock;
425
426         rp = (struct msft_rp_le_monitor_advertisement *)skb->data;
427         if (skb->len < sizeof(*rp)) {
428                 status = HCI_ERROR_UNSPECIFIED;
429                 goto unlock;
430         }
431
432         handle_data = kmalloc(sizeof(*handle_data), GFP_KERNEL);
433         if (!handle_data) {
434                 status = HCI_ERROR_UNSPECIFIED;
435                 goto unlock;
436         }
437
438         handle_data->mgmt_handle = monitor->handle;
439         handle_data->msft_handle = rp->handle;
440         INIT_LIST_HEAD(&handle_data->list);
441         list_add(&handle_data->list, &msft->handle_map);
442
443         monitor->state = ADV_MONITOR_STATE_OFFLOADED;
444
445 unlock:
446         if (status && monitor)
447                 hci_free_adv_monitor(hdev, monitor);
448
449         /* If in restart/reregister sequence, keep registering. */
450         if (msft->reregistering)
451                 reregister_monitor(hdev, msft->pending_add_handle + 1);
452
453         hci_dev_unlock(hdev);
454
455         if (!msft->reregistering)
456                 hci_add_adv_patterns_monitor_complete(hdev, status);
457 }
458
459 static void msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
460                                                     u8 status, u16 opcode,
461                                                     struct sk_buff *skb)
462 {
463         struct msft_cp_le_cancel_monitor_advertisement *cp;
464         struct msft_rp_le_cancel_monitor_advertisement *rp;
465         struct adv_monitor *monitor;
466         struct msft_monitor_advertisement_handle_data *handle_data;
467         struct msft_data *msft = hdev->msft_data;
468         int err;
469         bool pending;
470
471         if (status)
472                 goto done;
473
474         rp = (struct msft_rp_le_cancel_monitor_advertisement *)skb->data;
475         if (skb->len < sizeof(*rp)) {
476                 status = HCI_ERROR_UNSPECIFIED;
477                 goto done;
478         }
479
480         hci_dev_lock(hdev);
481
482         cp = hci_sent_cmd_data(hdev, hdev->msft_opcode);
483         handle_data = msft_find_handle_data(hdev, cp->handle, false);
484
485         if (handle_data) {
486                 monitor = idr_find(&hdev->adv_monitors_idr,
487                                    handle_data->mgmt_handle);
488
489                 if (monitor && monitor->state == ADV_MONITOR_STATE_OFFLOADED)
490                         monitor->state = ADV_MONITOR_STATE_REGISTERED;
491
492                 /* Do not free the monitor if it is being removed due to
493                  * suspend. It will be re-monitored on resume.
494                  */
495                 if (monitor && !msft->suspending)
496                         hci_free_adv_monitor(hdev, monitor);
497
498                 list_del(&handle_data->list);
499                 kfree(handle_data);
500         }
501
502         /* If in suspend/remove sequence, keep removing. */
503         if (msft->suspending)
504                 remove_monitor_on_suspend(hdev,
505                                           msft->pending_remove_handle + 1);
506
507         /* If remove all monitors is required, we need to continue the process
508          * here because the earlier it was paused when waiting for the
509          * response from controller.
510          */
511         if (msft->pending_remove_handle == 0) {
512                 pending = hci_remove_all_adv_monitor(hdev, &err);
513                 if (pending) {
514                         hci_dev_unlock(hdev);
515                         return;
516                 }
517
518                 if (err)
519                         status = HCI_ERROR_UNSPECIFIED;
520         }
521
522         hci_dev_unlock(hdev);
523
524 done:
525         if (!msft->suspending)
526                 hci_remove_adv_monitor_complete(hdev, status);
527 }
528
529 static void msft_le_set_advertisement_filter_enable_cb(struct hci_dev *hdev,
530                                                        u8 status, u16 opcode,
531                                                        struct sk_buff *skb)
532 {
533         struct msft_cp_le_set_advertisement_filter_enable *cp;
534         struct msft_rp_le_set_advertisement_filter_enable *rp;
535         struct msft_data *msft = hdev->msft_data;
536
537         rp = (struct msft_rp_le_set_advertisement_filter_enable *)skb->data;
538         if (skb->len < sizeof(*rp))
539                 return;
540
541         /* Error 0x0C would be returned if the filter enabled status is
542          * already set to whatever we were trying to set.
543          * Although the default state should be disabled, some controller set
544          * the initial value to enabled. Because there is no way to know the
545          * actual initial value before sending this command, here we also treat
546          * error 0x0C as success.
547          */
548         if (status != 0x00 && status != 0x0C)
549                 return;
550
551         hci_dev_lock(hdev);
552
553         cp = hci_sent_cmd_data(hdev, hdev->msft_opcode);
554         msft->filter_enabled = cp->enable;
555
556         if (status == 0x0C)
557                 bt_dev_warn(hdev, "MSFT filter_enable is already %s",
558                             cp->enable ? "on" : "off");
559
560         hci_dev_unlock(hdev);
561 }
562
563 static bool msft_monitor_rssi_valid(struct adv_monitor *monitor)
564 {
565         struct adv_rssi_thresholds *r = &monitor->rssi;
566
567         if (r->high_threshold < MSFT_RSSI_THRESHOLD_VALUE_MIN ||
568             r->high_threshold > MSFT_RSSI_THRESHOLD_VALUE_MAX ||
569             r->low_threshold < MSFT_RSSI_THRESHOLD_VALUE_MIN ||
570             r->low_threshold > MSFT_RSSI_THRESHOLD_VALUE_MAX)
571                 return false;
572
573         /* High_threshold_timeout is not supported,
574          * once high_threshold is reached, events are immediately reported.
575          */
576         if (r->high_threshold_timeout != 0)
577                 return false;
578
579         if (r->low_threshold_timeout > MSFT_RSSI_LOW_TIMEOUT_MAX)
580                 return false;
581
582         /* Sampling period from 0x00 to 0xFF are all allowed */
583         return true;
584 }
585
586 static bool msft_monitor_pattern_valid(struct adv_monitor *monitor)
587 {
588         return msft_monitor_rssi_valid(monitor);
589         /* No additional check needed for pattern-based monitor */
590 }
591
592 /* This function requires the caller holds hdev->lock */
593 static int __msft_add_monitor_pattern(struct hci_dev *hdev,
594                                       struct adv_monitor *monitor)
595 {
596         struct msft_cp_le_monitor_advertisement *cp;
597         struct msft_le_monitor_advertisement_pattern_data *pattern_data;
598         struct msft_le_monitor_advertisement_pattern *pattern;
599         struct adv_pattern *entry;
600         struct hci_request req;
601         struct msft_data *msft = hdev->msft_data;
602         size_t total_size = sizeof(*cp) + sizeof(*pattern_data);
603         ptrdiff_t offset = 0;
604         u8 pattern_count = 0;
605         int err = 0;
606
607         if (!msft_monitor_pattern_valid(monitor))
608                 return -EINVAL;
609
610         list_for_each_entry(entry, &monitor->patterns, list) {
611                 pattern_count++;
612                 total_size += sizeof(*pattern) + entry->length;
613         }
614
615         cp = kmalloc(total_size, GFP_KERNEL);
616         if (!cp)
617                 return -ENOMEM;
618
619         cp->sub_opcode = MSFT_OP_LE_MONITOR_ADVERTISEMENT;
620         cp->rssi_high = monitor->rssi.high_threshold;
621         cp->rssi_low = monitor->rssi.low_threshold;
622         cp->rssi_low_interval = (u8)monitor->rssi.low_threshold_timeout;
623         cp->rssi_sampling_period = monitor->rssi.sampling_period;
624
625         cp->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN;
626
627         pattern_data = (void *)cp->data;
628         pattern_data->count = pattern_count;
629
630         list_for_each_entry(entry, &monitor->patterns, list) {
631                 pattern = (void *)(pattern_data->data + offset);
632                 /* the length also includes data_type and offset */
633                 pattern->length = entry->length + 2;
634                 pattern->data_type = entry->ad_type;
635                 pattern->start_byte = entry->offset;
636                 memcpy(pattern->pattern, entry->value, entry->length);
637                 offset += sizeof(*pattern) + entry->length;
638         }
639
640         hci_req_init(&req, hdev);
641         hci_req_add(&req, hdev->msft_opcode, total_size, cp);
642         err = hci_req_run_skb(&req, msft_le_monitor_advertisement_cb);
643         kfree(cp);
644
645         if (!err)
646                 msft->pending_add_handle = monitor->handle;
647
648         return err;
649 }
650
651 /* This function requires the caller holds hdev->lock */
652 int msft_add_monitor_pattern(struct hci_dev *hdev, struct adv_monitor *monitor)
653 {
654         struct msft_data *msft = hdev->msft_data;
655
656         if (!msft)
657                 return -EOPNOTSUPP;
658
659         if (msft->reregistering || msft->suspending)
660                 return -EBUSY;
661
662         return __msft_add_monitor_pattern(hdev, monitor);
663 }
664
665 /* This function requires the caller holds hdev->lock */
666 static int __msft_remove_monitor(struct hci_dev *hdev,
667                                  struct adv_monitor *monitor, u16 handle)
668 {
669         struct msft_cp_le_cancel_monitor_advertisement cp;
670         struct msft_monitor_advertisement_handle_data *handle_data;
671         struct hci_request req;
672         struct msft_data *msft = hdev->msft_data;
673         int err = 0;
674
675         handle_data = msft_find_handle_data(hdev, monitor->handle, true);
676
677         /* If no matched handle, just remove without telling controller */
678         if (!handle_data)
679                 return -ENOENT;
680
681         cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
682         cp.handle = handle_data->msft_handle;
683
684         hci_req_init(&req, hdev);
685         hci_req_add(&req, hdev->msft_opcode, sizeof(cp), &cp);
686         err = hci_req_run_skb(&req, msft_le_cancel_monitor_advertisement_cb);
687
688         if (!err)
689                 msft->pending_remove_handle = handle;
690
691         return err;
692 }
693
694 /* This function requires the caller holds hdev->lock */
695 int msft_remove_monitor(struct hci_dev *hdev, struct adv_monitor *monitor,
696                         u16 handle)
697 {
698         struct msft_data *msft = hdev->msft_data;
699
700         if (!msft)
701                 return -EOPNOTSUPP;
702
703         if (msft->reregistering || msft->suspending)
704                 return -EBUSY;
705
706         return __msft_remove_monitor(hdev, monitor, handle);
707 }
708
709 void msft_req_add_set_filter_enable(struct hci_request *req, bool enable)
710 {
711         struct hci_dev *hdev = req->hdev;
712         struct msft_cp_le_set_advertisement_filter_enable cp;
713
714         cp.sub_opcode = MSFT_OP_LE_SET_ADVERTISEMENT_FILTER_ENABLE;
715         cp.enable = enable;
716
717         hci_req_add(req, hdev->msft_opcode, sizeof(cp), &cp);
718 }
719
720 int msft_set_filter_enable(struct hci_dev *hdev, bool enable)
721 {
722         struct hci_request req;
723         struct msft_data *msft = hdev->msft_data;
724         int err;
725
726         if (!msft)
727                 return -EOPNOTSUPP;
728
729         hci_req_init(&req, hdev);
730         msft_req_add_set_filter_enable(&req, enable);
731         err = hci_req_run_skb(&req, msft_le_set_advertisement_filter_enable_cb);
732
733         return err;
734 }
735
736 bool msft_curve_validity(struct hci_dev *hdev)
737 {
738         return hdev->msft_curve_validity;
739 }
This page took 0.073404 seconds and 4 git commands to generate.