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 | |
14 | #define HDEV_PARAM_U16(_param_name_) \ |
15 | struct {\ |
16 | struct mgmt_tlv entry; \ |
17 | __le16 value; \ |
18 | } __packed _param_name_ |
19 | |
20 | #define HDEV_PARAM_U8(_param_name_) \ |
21 | struct {\ |
22 | struct mgmt_tlv entry; \ |
23 | __u8 value; \ |
24 | } __packed _param_name_ |
25 | |
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 | |
32 | #define TLV_SET_U8(_param_code_, _param_name_) \ |
33 | { \ |
34 | { cpu_to_le16(_param_code_), sizeof(__u8) }, \ |
35 | hdev->_param_name_ \ |
36 | } |
37 | |
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 | } |
43 | |
44 | int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data, |
45 | u16 data_len) |
46 | { |
47 | int ret; |
48 | struct mgmt_rp_read_def_system_config { |
49 | /* Please see mgmt-api.txt for documentation of these values */ |
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); |
80 | HDEV_PARAM_U8(enable_advmon_interleave_scan); |
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), |
113 | TLV_SET_U8(0x001f, enable_advmon_interleave_scan), |
114 | }; |
115 | |
116 | bt_dev_dbg(hdev, "sock %p" , sk); |
117 | |
118 | ret = mgmt_cmd_complete(sk, index: hdev->id, |
119 | MGMT_OP_READ_DEF_SYSTEM_CONFIG, |
120 | status: 0, rp: &rp, rp_len: sizeof(rp)); |
121 | return ret; |
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))) |
126 | #define TLV_GET_U8(tlv) (*((__u8 *)(TO_TLV(tlv)->value))) |
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, index: 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; |
143 | size_t exp_type_len; |
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) { |
149 | bt_dev_warn(hdev, "invalid len left %u, exp >= %u" , |
150 | buffer_left, exp_len); |
151 | |
152 | return mgmt_cmd_status(sk, index: 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: |
186 | case 0x001b: |
187 | case 0x001d: |
188 | case 0x001e: |
189 | exp_type_len = sizeof(u16); |
190 | break; |
191 | case 0x001f: |
192 | exp_type_len = sizeof(u8); |
193 | break; |
194 | default: |
195 | exp_type_len = 0; |
196 | bt_dev_warn(hdev, "unsupported parameter %u" , type); |
197 | break; |
198 | } |
199 | |
200 | if (exp_type_len && len != exp_type_len) { |
201 | bt_dev_warn(hdev, "invalid length %d, exp %zu for type %u" , |
202 | len, exp_type_len, type); |
203 | |
204 | return mgmt_cmd_status(sk, index: hdev->id, |
205 | MGMT_OP_SET_DEF_SYSTEM_CONFIG, |
206 | MGMT_STATUS_INVALID_PARAMS); |
207 | } |
208 | |
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; |
304 | case 0x0001b: |
305 | hdev->def_le_autoconnect_timeout = |
306 | msecs_to_jiffies(TLV_GET_LE16(buffer)); |
307 | break; |
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; |
314 | case 0x0001f: |
315 | hdev->enable_advmon_interleave_scan = TLV_GET_U8(buffer); |
316 | break; |
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 | |
326 | return mgmt_cmd_complete(sk, index: hdev->id, |
327 | MGMT_OP_SET_DEF_SYSTEM_CONFIG, status: 0, NULL, rp_len: 0); |
328 | } |
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, index: hdev->id, |
336 | MGMT_OP_READ_DEF_RUNTIME_CONFIG, status: 0, NULL, rp_len: 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, index: hdev->id, MGMT_OP_SET_DEF_SYSTEM_CONFIG, |
345 | MGMT_STATUS_INVALID_PARAMS); |
346 | } |
347 | |