]>
Commit | Line | Data |
---|---|---|
17896406 AM |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | ||
3 | /* | |
4 | * Copyright (C) 2020 Google Corporation | |
5 | */ | |
6 | ||
7 | #include <net/bluetooth/bluetooth.h> | |
8 | #include <net/bluetooth/hci_core.h> | |
9 | #include <net/bluetooth/mgmt.h> | |
10 | ||
11 | #include "mgmt_util.h" | |
12 | #include "mgmt_config.h" | |
13 | ||
3bc615fa HC |
14 | #define HDEV_PARAM_U16(_param_name_) \ |
15 | struct {\ | |
16 | struct mgmt_tlv entry; \ | |
17 | __le16 value; \ | |
18 | } __packed _param_name_ | |
17896406 | 19 | |
80af16a3 HC |
20 | #define HDEV_PARAM_U8(_param_name_) \ |
21 | struct {\ | |
22 | struct mgmt_tlv entry; \ | |
23 | __u8 value; \ | |
24 | } __packed _param_name_ | |
25 | ||
3bc615fa HC |
26 | #define TLV_SET_U16(_param_code_, _param_name_) \ |
27 | { \ | |
28 | { cpu_to_le16(_param_code_), sizeof(__u16) }, \ | |
29 | cpu_to_le16(hdev->_param_name_) \ | |
30 | } | |
31 | ||
80af16a3 HC |
32 | #define TLV_SET_U8(_param_code_, _param_name_) \ |
33 | { \ | |
34 | { cpu_to_le16(_param_code_), sizeof(__u8) }, \ | |
35 | hdev->_param_name_ \ | |
36 | } | |
37 | ||
3bc615fa HC |
38 | #define TLV_SET_U16_JIFFIES_TO_MSECS(_param_code_, _param_name_) \ |
39 | { \ | |
40 | { cpu_to_le16(_param_code_), sizeof(__u16) }, \ | |
41 | cpu_to_le16(jiffies_to_msecs(hdev->_param_name_)) \ | |
42 | } | |
49b020c1 | 43 | |
17896406 AM |
44 | int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, |
45 | u16 data_len) | |
46 | { | |
3bc615fa HC |
47 | int ret; |
48 | struct mgmt_rp_read_def_system_config { | |
17896406 | 49 | /* Please see mgmt-api.txt for documentation of these values */ |
3bc615fa HC |
50 | HDEV_PARAM_U16(def_page_scan_type); |
51 | HDEV_PARAM_U16(def_page_scan_int); | |
52 | HDEV_PARAM_U16(def_page_scan_window); | |
53 | HDEV_PARAM_U16(def_inq_scan_type); | |
54 | HDEV_PARAM_U16(def_inq_scan_int); | |
55 | HDEV_PARAM_U16(def_inq_scan_window); | |
56 | HDEV_PARAM_U16(def_br_lsto); | |
57 | HDEV_PARAM_U16(def_page_timeout); | |
58 | HDEV_PARAM_U16(sniff_min_interval); | |
59 | HDEV_PARAM_U16(sniff_max_interval); | |
60 | HDEV_PARAM_U16(le_adv_min_interval); | |
61 | HDEV_PARAM_U16(le_adv_max_interval); | |
62 | HDEV_PARAM_U16(def_multi_adv_rotation_duration); | |
63 | HDEV_PARAM_U16(le_scan_interval); | |
64 | HDEV_PARAM_U16(le_scan_window); | |
65 | HDEV_PARAM_U16(le_scan_int_suspend); | |
66 | HDEV_PARAM_U16(le_scan_window_suspend); | |
67 | HDEV_PARAM_U16(le_scan_int_discovery); | |
68 | HDEV_PARAM_U16(le_scan_window_discovery); | |
69 | HDEV_PARAM_U16(le_scan_int_adv_monitor); | |
70 | HDEV_PARAM_U16(le_scan_window_adv_monitor); | |
71 | HDEV_PARAM_U16(le_scan_int_connect); | |
72 | HDEV_PARAM_U16(le_scan_window_connect); | |
73 | HDEV_PARAM_U16(le_conn_min_interval); | |
74 | HDEV_PARAM_U16(le_conn_max_interval); | |
75 | HDEV_PARAM_U16(le_conn_latency); | |
76 | HDEV_PARAM_U16(le_supv_timeout); | |
77 | HDEV_PARAM_U16(def_le_autoconnect_timeout); | |
78 | HDEV_PARAM_U16(advmon_allowlist_duration); | |
79 | HDEV_PARAM_U16(advmon_no_filter_duration); | |
80af16a3 | 80 | HDEV_PARAM_U8(enable_advmon_interleave_scan); |
3bc615fa HC |
81 | } __packed rp = { |
82 | TLV_SET_U16(0x0000, def_page_scan_type), | |
83 | TLV_SET_U16(0x0001, def_page_scan_int), | |
84 | TLV_SET_U16(0x0002, def_page_scan_window), | |
85 | TLV_SET_U16(0x0003, def_inq_scan_type), | |
86 | TLV_SET_U16(0x0004, def_inq_scan_int), | |
87 | TLV_SET_U16(0x0005, def_inq_scan_window), | |
88 | TLV_SET_U16(0x0006, def_br_lsto), | |
89 | TLV_SET_U16(0x0007, def_page_timeout), | |
90 | TLV_SET_U16(0x0008, sniff_min_interval), | |
91 | TLV_SET_U16(0x0009, sniff_max_interval), | |
92 | TLV_SET_U16(0x000a, le_adv_min_interval), | |
93 | TLV_SET_U16(0x000b, le_adv_max_interval), | |
94 | TLV_SET_U16(0x000c, def_multi_adv_rotation_duration), | |
95 | TLV_SET_U16(0x000d, le_scan_interval), | |
96 | TLV_SET_U16(0x000e, le_scan_window), | |
97 | TLV_SET_U16(0x000f, le_scan_int_suspend), | |
98 | TLV_SET_U16(0x0010, le_scan_window_suspend), | |
99 | TLV_SET_U16(0x0011, le_scan_int_discovery), | |
100 | TLV_SET_U16(0x0012, le_scan_window_discovery), | |
101 | TLV_SET_U16(0x0013, le_scan_int_adv_monitor), | |
102 | TLV_SET_U16(0x0014, le_scan_window_adv_monitor), | |
103 | TLV_SET_U16(0x0015, le_scan_int_connect), | |
104 | TLV_SET_U16(0x0016, le_scan_window_connect), | |
105 | TLV_SET_U16(0x0017, le_conn_min_interval), | |
106 | TLV_SET_U16(0x0018, le_conn_max_interval), | |
107 | TLV_SET_U16(0x0019, le_conn_latency), | |
108 | TLV_SET_U16(0x001a, le_supv_timeout), | |
109 | TLV_SET_U16_JIFFIES_TO_MSECS(0x001b, | |
110 | def_le_autoconnect_timeout), | |
111 | TLV_SET_U16(0x001d, advmon_allowlist_duration), | |
112 | TLV_SET_U16(0x001e, advmon_no_filter_duration), | |
80af16a3 | 113 | TLV_SET_U8(0x001f, enable_advmon_interleave_scan), |
17896406 | 114 | }; |
17896406 AM |
115 | |
116 | bt_dev_dbg(hdev, "sock %p", sk); | |
117 | ||
3bc615fa HC |
118 | ret = mgmt_cmd_complete(sk, hdev->id, |
119 | MGMT_OP_READ_DEF_SYSTEM_CONFIG, | |
120 | 0, &rp, sizeof(rp)); | |
121 | return ret; | |
17896406 AM |
122 | } |
123 | ||
124 | #define TO_TLV(x) ((struct mgmt_tlv *)(x)) | |
125 | #define TLV_GET_LE16(tlv) le16_to_cpu(*((__le16 *)(TO_TLV(tlv)->value))) | |
80af16a3 | 126 | #define TLV_GET_U8(tlv) (*((__u8 *)(TO_TLV(tlv)->value))) |
17896406 AM |
127 | |
128 | int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, | |
129 | u16 data_len) | |
130 | { | |
131 | u16 buffer_left = data_len; | |
132 | u8 *buffer = data; | |
133 | ||
134 | if (buffer_left < sizeof(struct mgmt_tlv)) { | |
135 | return mgmt_cmd_status(sk, hdev->id, | |
136 | MGMT_OP_SET_DEF_SYSTEM_CONFIG, | |
137 | MGMT_STATUS_INVALID_PARAMS); | |
138 | } | |
139 | ||
140 | /* First pass to validate the tlv */ | |
141 | while (buffer_left >= sizeof(struct mgmt_tlv)) { | |
142 | const u8 len = TO_TLV(buffer)->length; | |
80af16a3 | 143 | size_t exp_type_len; |
17896406 AM |
144 | const u16 exp_len = sizeof(struct mgmt_tlv) + |
145 | len; | |
146 | const u16 type = le16_to_cpu(TO_TLV(buffer)->type); | |
147 | ||
148 | if (buffer_left < exp_len) { | |
85d67284 | 149 | bt_dev_warn(hdev, "invalid len left %u, exp >= %u", |
17896406 AM |
150 | buffer_left, exp_len); |
151 | ||
152 | return mgmt_cmd_status(sk, hdev->id, | |
153 | MGMT_OP_SET_DEF_SYSTEM_CONFIG, | |
154 | MGMT_STATUS_INVALID_PARAMS); | |
155 | } | |
156 | ||
157 | /* Please see mgmt-api.txt for documentation of these values */ | |
158 | switch (type) { | |
159 | case 0x0000: | |
160 | case 0x0001: | |
161 | case 0x0002: | |
162 | case 0x0003: | |
163 | case 0x0004: | |
164 | case 0x0005: | |
165 | case 0x0006: | |
166 | case 0x0007: | |
167 | case 0x0008: | |
168 | case 0x0009: | |
169 | case 0x000a: | |
170 | case 0x000b: | |
171 | case 0x000c: | |
172 | case 0x000d: | |
173 | case 0x000e: | |
174 | case 0x000f: | |
175 | case 0x0010: | |
176 | case 0x0011: | |
177 | case 0x0012: | |
178 | case 0x0013: | |
179 | case 0x0014: | |
180 | case 0x0015: | |
181 | case 0x0016: | |
182 | case 0x0017: | |
183 | case 0x0018: | |
184 | case 0x0019: | |
185 | case 0x001a: | |
49b020c1 | 186 | case 0x001b: |
c4f1f408 HC |
187 | case 0x001d: |
188 | case 0x001e: | |
80af16a3 HC |
189 | exp_type_len = sizeof(u16); |
190 | break; | |
191 | case 0x001f: | |
192 | exp_type_len = sizeof(u8); | |
17896406 AM |
193 | break; |
194 | default: | |
80af16a3 | 195 | exp_type_len = 0; |
17896406 AM |
196 | bt_dev_warn(hdev, "unsupported parameter %u", type); |
197 | break; | |
198 | } | |
199 | ||
80af16a3 | 200 | if (exp_type_len && len != exp_type_len) { |
85d67284 | 201 | bt_dev_warn(hdev, "invalid length %d, exp %zu for type %u", |
80af16a3 HC |
202 | len, exp_type_len, type); |
203 | ||
204 | return mgmt_cmd_status(sk, hdev->id, | |
205 | MGMT_OP_SET_DEF_SYSTEM_CONFIG, | |
206 | MGMT_STATUS_INVALID_PARAMS); | |
207 | } | |
208 | ||
17896406 AM |
209 | buffer_left -= exp_len; |
210 | buffer += exp_len; | |
211 | } | |
212 | ||
213 | buffer_left = data_len; | |
214 | buffer = data; | |
215 | while (buffer_left >= sizeof(struct mgmt_tlv)) { | |
216 | const u8 len = TO_TLV(buffer)->length; | |
217 | const u16 exp_len = sizeof(struct mgmt_tlv) + | |
218 | len; | |
219 | const u16 type = le16_to_cpu(TO_TLV(buffer)->type); | |
220 | ||
221 | switch (type) { | |
222 | case 0x0000: | |
223 | hdev->def_page_scan_type = TLV_GET_LE16(buffer); | |
224 | break; | |
225 | case 0x0001: | |
226 | hdev->def_page_scan_int = TLV_GET_LE16(buffer); | |
227 | break; | |
228 | case 0x0002: | |
229 | hdev->def_page_scan_window = TLV_GET_LE16(buffer); | |
230 | break; | |
231 | case 0x0003: | |
232 | hdev->def_inq_scan_type = TLV_GET_LE16(buffer); | |
233 | break; | |
234 | case 0x0004: | |
235 | hdev->def_inq_scan_int = TLV_GET_LE16(buffer); | |
236 | break; | |
237 | case 0x0005: | |
238 | hdev->def_inq_scan_window = TLV_GET_LE16(buffer); | |
239 | break; | |
240 | case 0x0006: | |
241 | hdev->def_br_lsto = TLV_GET_LE16(buffer); | |
242 | break; | |
243 | case 0x0007: | |
244 | hdev->def_page_timeout = TLV_GET_LE16(buffer); | |
245 | break; | |
246 | case 0x0008: | |
247 | hdev->sniff_min_interval = TLV_GET_LE16(buffer); | |
248 | break; | |
249 | case 0x0009: | |
250 | hdev->sniff_max_interval = TLV_GET_LE16(buffer); | |
251 | break; | |
252 | case 0x000a: | |
253 | hdev->le_adv_min_interval = TLV_GET_LE16(buffer); | |
254 | break; | |
255 | case 0x000b: | |
256 | hdev->le_adv_max_interval = TLV_GET_LE16(buffer); | |
257 | break; | |
258 | case 0x000c: | |
259 | hdev->def_multi_adv_rotation_duration = | |
260 | TLV_GET_LE16(buffer); | |
261 | break; | |
262 | case 0x000d: | |
263 | hdev->le_scan_interval = TLV_GET_LE16(buffer); | |
264 | break; | |
265 | case 0x000e: | |
266 | hdev->le_scan_window = TLV_GET_LE16(buffer); | |
267 | break; | |
268 | case 0x000f: | |
269 | hdev->le_scan_int_suspend = TLV_GET_LE16(buffer); | |
270 | break; | |
271 | case 0x0010: | |
272 | hdev->le_scan_window_suspend = TLV_GET_LE16(buffer); | |
273 | break; | |
274 | case 0x0011: | |
275 | hdev->le_scan_int_discovery = TLV_GET_LE16(buffer); | |
276 | break; | |
277 | case 0x00012: | |
278 | hdev->le_scan_window_discovery = TLV_GET_LE16(buffer); | |
279 | break; | |
280 | case 0x00013: | |
281 | hdev->le_scan_int_adv_monitor = TLV_GET_LE16(buffer); | |
282 | break; | |
283 | case 0x00014: | |
284 | hdev->le_scan_window_adv_monitor = TLV_GET_LE16(buffer); | |
285 | break; | |
286 | case 0x00015: | |
287 | hdev->le_scan_int_connect = TLV_GET_LE16(buffer); | |
288 | break; | |
289 | case 0x00016: | |
290 | hdev->le_scan_window_connect = TLV_GET_LE16(buffer); | |
291 | break; | |
292 | case 0x00017: | |
293 | hdev->le_conn_min_interval = TLV_GET_LE16(buffer); | |
294 | break; | |
295 | case 0x00018: | |
296 | hdev->le_conn_max_interval = TLV_GET_LE16(buffer); | |
297 | break; | |
298 | case 0x00019: | |
299 | hdev->le_conn_latency = TLV_GET_LE16(buffer); | |
300 | break; | |
301 | case 0x0001a: | |
302 | hdev->le_supv_timeout = TLV_GET_LE16(buffer); | |
303 | break; | |
49b020c1 AM |
304 | case 0x0001b: |
305 | hdev->def_le_autoconnect_timeout = | |
306 | msecs_to_jiffies(TLV_GET_LE16(buffer)); | |
307 | break; | |
c4f1f408 HC |
308 | case 0x0001d: |
309 | hdev->advmon_allowlist_duration = TLV_GET_LE16(buffer); | |
310 | break; | |
311 | case 0x0001e: | |
312 | hdev->advmon_no_filter_duration = TLV_GET_LE16(buffer); | |
313 | break; | |
80af16a3 HC |
314 | case 0x0001f: |
315 | hdev->enable_advmon_interleave_scan = TLV_GET_U8(buffer); | |
316 | break; | |
17896406 AM |
317 | default: |
318 | bt_dev_warn(hdev, "unsupported parameter %u", type); | |
319 | break; | |
320 | } | |
321 | ||
322 | buffer_left -= exp_len; | |
323 | buffer += exp_len; | |
324 | } | |
325 | ||
46605a27 MH |
326 | return mgmt_cmd_complete(sk, hdev->id, |
327 | MGMT_OP_SET_DEF_SYSTEM_CONFIG, 0, NULL, 0); | |
17896406 | 328 | } |
aececa64 MH |
329 | |
330 | int read_def_runtime_config(struct sock *sk, struct hci_dev *hdev, void *data, | |
331 | u16 data_len) | |
332 | { | |
333 | bt_dev_dbg(hdev, "sock %p", sk); | |
334 | ||
335 | return mgmt_cmd_complete(sk, hdev->id, | |
336 | MGMT_OP_READ_DEF_RUNTIME_CONFIG, 0, NULL, 0); | |
337 | } | |
338 | ||
339 | int set_def_runtime_config(struct sock *sk, struct hci_dev *hdev, void *data, | |
340 | u16 data_len) | |
341 | { | |
342 | bt_dev_dbg(hdev, "sock %p", sk); | |
343 | ||
344 | return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEF_SYSTEM_CONFIG, | |
345 | MGMT_STATUS_INVALID_PARAMS); | |
346 | } |