1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun /*
4*4882a593Smuzhiyun * Copyright (C) 2020 Google Corporation
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <net/bluetooth/bluetooth.h>
8*4882a593Smuzhiyun #include <net/bluetooth/hci_core.h>
9*4882a593Smuzhiyun #include <net/bluetooth/mgmt.h>
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include "mgmt_util.h"
12*4882a593Smuzhiyun #include "mgmt_config.h"
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #define HDEV_PARAM_U16(_param_code_, _param_name_) \
15*4882a593Smuzhiyun { \
16*4882a593Smuzhiyun { cpu_to_le16(_param_code_), sizeof(__u16) }, \
17*4882a593Smuzhiyun { cpu_to_le16(hdev->_param_name_) } \
18*4882a593Smuzhiyun }
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define HDEV_PARAM_U16_JIFFIES_TO_MSECS(_param_code_, _param_name_) \
21*4882a593Smuzhiyun { \
22*4882a593Smuzhiyun { cpu_to_le16(_param_code_), sizeof(__u16) }, \
23*4882a593Smuzhiyun { cpu_to_le16(jiffies_to_msecs(hdev->_param_name_)) } \
24*4882a593Smuzhiyun }
25*4882a593Smuzhiyun
read_def_system_config(struct sock * sk,struct hci_dev * hdev,void * data,u16 data_len)26*4882a593Smuzhiyun int read_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
27*4882a593Smuzhiyun u16 data_len)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun struct {
30*4882a593Smuzhiyun struct mgmt_tlv entry;
31*4882a593Smuzhiyun union {
32*4882a593Smuzhiyun /* This is a simplification for now since all values
33*4882a593Smuzhiyun * are 16 bits. In the future, this code may need
34*4882a593Smuzhiyun * refactoring to account for variable length values
35*4882a593Smuzhiyun * and properly calculate the required buffer size.
36*4882a593Smuzhiyun */
37*4882a593Smuzhiyun __le16 value;
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun } __packed params[] = {
40*4882a593Smuzhiyun /* Please see mgmt-api.txt for documentation of these values */
41*4882a593Smuzhiyun HDEV_PARAM_U16(0x0000, def_page_scan_type),
42*4882a593Smuzhiyun HDEV_PARAM_U16(0x0001, def_page_scan_int),
43*4882a593Smuzhiyun HDEV_PARAM_U16(0x0002, def_page_scan_window),
44*4882a593Smuzhiyun HDEV_PARAM_U16(0x0003, def_inq_scan_type),
45*4882a593Smuzhiyun HDEV_PARAM_U16(0x0004, def_inq_scan_int),
46*4882a593Smuzhiyun HDEV_PARAM_U16(0x0005, def_inq_scan_window),
47*4882a593Smuzhiyun HDEV_PARAM_U16(0x0006, def_br_lsto),
48*4882a593Smuzhiyun HDEV_PARAM_U16(0x0007, def_page_timeout),
49*4882a593Smuzhiyun HDEV_PARAM_U16(0x0008, sniff_min_interval),
50*4882a593Smuzhiyun HDEV_PARAM_U16(0x0009, sniff_max_interval),
51*4882a593Smuzhiyun HDEV_PARAM_U16(0x000a, le_adv_min_interval),
52*4882a593Smuzhiyun HDEV_PARAM_U16(0x000b, le_adv_max_interval),
53*4882a593Smuzhiyun HDEV_PARAM_U16(0x000c, def_multi_adv_rotation_duration),
54*4882a593Smuzhiyun HDEV_PARAM_U16(0x000d, le_scan_interval),
55*4882a593Smuzhiyun HDEV_PARAM_U16(0x000e, le_scan_window),
56*4882a593Smuzhiyun HDEV_PARAM_U16(0x000f, le_scan_int_suspend),
57*4882a593Smuzhiyun HDEV_PARAM_U16(0x0010, le_scan_window_suspend),
58*4882a593Smuzhiyun HDEV_PARAM_U16(0x0011, le_scan_int_discovery),
59*4882a593Smuzhiyun HDEV_PARAM_U16(0x0012, le_scan_window_discovery),
60*4882a593Smuzhiyun HDEV_PARAM_U16(0x0013, le_scan_int_adv_monitor),
61*4882a593Smuzhiyun HDEV_PARAM_U16(0x0014, le_scan_window_adv_monitor),
62*4882a593Smuzhiyun HDEV_PARAM_U16(0x0015, le_scan_int_connect),
63*4882a593Smuzhiyun HDEV_PARAM_U16(0x0016, le_scan_window_connect),
64*4882a593Smuzhiyun HDEV_PARAM_U16(0x0017, le_conn_min_interval),
65*4882a593Smuzhiyun HDEV_PARAM_U16(0x0018, le_conn_max_interval),
66*4882a593Smuzhiyun HDEV_PARAM_U16(0x0019, le_conn_latency),
67*4882a593Smuzhiyun HDEV_PARAM_U16(0x001a, le_supv_timeout),
68*4882a593Smuzhiyun HDEV_PARAM_U16_JIFFIES_TO_MSECS(0x001b,
69*4882a593Smuzhiyun def_le_autoconnect_timeout),
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun struct mgmt_rp_read_def_system_config *rp = (void *)params;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun bt_dev_dbg(hdev, "sock %p", sk);
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun return mgmt_cmd_complete(sk, hdev->id,
76*4882a593Smuzhiyun MGMT_OP_READ_DEF_SYSTEM_CONFIG,
77*4882a593Smuzhiyun 0, rp, sizeof(params));
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun #define TO_TLV(x) ((struct mgmt_tlv *)(x))
81*4882a593Smuzhiyun #define TLV_GET_LE16(tlv) le16_to_cpu(*((__le16 *)(TO_TLV(tlv)->value)))
82*4882a593Smuzhiyun
set_def_system_config(struct sock * sk,struct hci_dev * hdev,void * data,u16 data_len)83*4882a593Smuzhiyun int set_def_system_config(struct sock *sk, struct hci_dev *hdev, void *data,
84*4882a593Smuzhiyun u16 data_len)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun u16 buffer_left = data_len;
87*4882a593Smuzhiyun u8 *buffer = data;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun if (buffer_left < sizeof(struct mgmt_tlv)) {
90*4882a593Smuzhiyun return mgmt_cmd_status(sk, hdev->id,
91*4882a593Smuzhiyun MGMT_OP_SET_DEF_SYSTEM_CONFIG,
92*4882a593Smuzhiyun MGMT_STATUS_INVALID_PARAMS);
93*4882a593Smuzhiyun }
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun /* First pass to validate the tlv */
96*4882a593Smuzhiyun while (buffer_left >= sizeof(struct mgmt_tlv)) {
97*4882a593Smuzhiyun const u8 len = TO_TLV(buffer)->length;
98*4882a593Smuzhiyun const u16 exp_len = sizeof(struct mgmt_tlv) +
99*4882a593Smuzhiyun len;
100*4882a593Smuzhiyun const u16 type = le16_to_cpu(TO_TLV(buffer)->type);
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun if (buffer_left < exp_len) {
103*4882a593Smuzhiyun bt_dev_warn(hdev, "invalid len left %d, exp >= %d",
104*4882a593Smuzhiyun buffer_left, exp_len);
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun return mgmt_cmd_status(sk, hdev->id,
107*4882a593Smuzhiyun MGMT_OP_SET_DEF_SYSTEM_CONFIG,
108*4882a593Smuzhiyun MGMT_STATUS_INVALID_PARAMS);
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun /* Please see mgmt-api.txt for documentation of these values */
112*4882a593Smuzhiyun switch (type) {
113*4882a593Smuzhiyun case 0x0000:
114*4882a593Smuzhiyun case 0x0001:
115*4882a593Smuzhiyun case 0x0002:
116*4882a593Smuzhiyun case 0x0003:
117*4882a593Smuzhiyun case 0x0004:
118*4882a593Smuzhiyun case 0x0005:
119*4882a593Smuzhiyun case 0x0006:
120*4882a593Smuzhiyun case 0x0007:
121*4882a593Smuzhiyun case 0x0008:
122*4882a593Smuzhiyun case 0x0009:
123*4882a593Smuzhiyun case 0x000a:
124*4882a593Smuzhiyun case 0x000b:
125*4882a593Smuzhiyun case 0x000c:
126*4882a593Smuzhiyun case 0x000d:
127*4882a593Smuzhiyun case 0x000e:
128*4882a593Smuzhiyun case 0x000f:
129*4882a593Smuzhiyun case 0x0010:
130*4882a593Smuzhiyun case 0x0011:
131*4882a593Smuzhiyun case 0x0012:
132*4882a593Smuzhiyun case 0x0013:
133*4882a593Smuzhiyun case 0x0014:
134*4882a593Smuzhiyun case 0x0015:
135*4882a593Smuzhiyun case 0x0016:
136*4882a593Smuzhiyun case 0x0017:
137*4882a593Smuzhiyun case 0x0018:
138*4882a593Smuzhiyun case 0x0019:
139*4882a593Smuzhiyun case 0x001a:
140*4882a593Smuzhiyun case 0x001b:
141*4882a593Smuzhiyun if (len != sizeof(u16)) {
142*4882a593Smuzhiyun bt_dev_warn(hdev, "invalid length %d, exp %zu for type %d",
143*4882a593Smuzhiyun len, sizeof(u16), type);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun return mgmt_cmd_status(sk, hdev->id,
146*4882a593Smuzhiyun MGMT_OP_SET_DEF_SYSTEM_CONFIG,
147*4882a593Smuzhiyun MGMT_STATUS_INVALID_PARAMS);
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun break;
150*4882a593Smuzhiyun default:
151*4882a593Smuzhiyun bt_dev_warn(hdev, "unsupported parameter %u", type);
152*4882a593Smuzhiyun break;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun buffer_left -= exp_len;
156*4882a593Smuzhiyun buffer += exp_len;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun buffer_left = data_len;
160*4882a593Smuzhiyun buffer = data;
161*4882a593Smuzhiyun while (buffer_left >= sizeof(struct mgmt_tlv)) {
162*4882a593Smuzhiyun const u8 len = TO_TLV(buffer)->length;
163*4882a593Smuzhiyun const u16 exp_len = sizeof(struct mgmt_tlv) +
164*4882a593Smuzhiyun len;
165*4882a593Smuzhiyun const u16 type = le16_to_cpu(TO_TLV(buffer)->type);
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun switch (type) {
168*4882a593Smuzhiyun case 0x0000:
169*4882a593Smuzhiyun hdev->def_page_scan_type = TLV_GET_LE16(buffer);
170*4882a593Smuzhiyun break;
171*4882a593Smuzhiyun case 0x0001:
172*4882a593Smuzhiyun hdev->def_page_scan_int = TLV_GET_LE16(buffer);
173*4882a593Smuzhiyun break;
174*4882a593Smuzhiyun case 0x0002:
175*4882a593Smuzhiyun hdev->def_page_scan_window = TLV_GET_LE16(buffer);
176*4882a593Smuzhiyun break;
177*4882a593Smuzhiyun case 0x0003:
178*4882a593Smuzhiyun hdev->def_inq_scan_type = TLV_GET_LE16(buffer);
179*4882a593Smuzhiyun break;
180*4882a593Smuzhiyun case 0x0004:
181*4882a593Smuzhiyun hdev->def_inq_scan_int = TLV_GET_LE16(buffer);
182*4882a593Smuzhiyun break;
183*4882a593Smuzhiyun case 0x0005:
184*4882a593Smuzhiyun hdev->def_inq_scan_window = TLV_GET_LE16(buffer);
185*4882a593Smuzhiyun break;
186*4882a593Smuzhiyun case 0x0006:
187*4882a593Smuzhiyun hdev->def_br_lsto = TLV_GET_LE16(buffer);
188*4882a593Smuzhiyun break;
189*4882a593Smuzhiyun case 0x0007:
190*4882a593Smuzhiyun hdev->def_page_timeout = TLV_GET_LE16(buffer);
191*4882a593Smuzhiyun break;
192*4882a593Smuzhiyun case 0x0008:
193*4882a593Smuzhiyun hdev->sniff_min_interval = TLV_GET_LE16(buffer);
194*4882a593Smuzhiyun break;
195*4882a593Smuzhiyun case 0x0009:
196*4882a593Smuzhiyun hdev->sniff_max_interval = TLV_GET_LE16(buffer);
197*4882a593Smuzhiyun break;
198*4882a593Smuzhiyun case 0x000a:
199*4882a593Smuzhiyun hdev->le_adv_min_interval = TLV_GET_LE16(buffer);
200*4882a593Smuzhiyun break;
201*4882a593Smuzhiyun case 0x000b:
202*4882a593Smuzhiyun hdev->le_adv_max_interval = TLV_GET_LE16(buffer);
203*4882a593Smuzhiyun break;
204*4882a593Smuzhiyun case 0x000c:
205*4882a593Smuzhiyun hdev->def_multi_adv_rotation_duration =
206*4882a593Smuzhiyun TLV_GET_LE16(buffer);
207*4882a593Smuzhiyun break;
208*4882a593Smuzhiyun case 0x000d:
209*4882a593Smuzhiyun hdev->le_scan_interval = TLV_GET_LE16(buffer);
210*4882a593Smuzhiyun break;
211*4882a593Smuzhiyun case 0x000e:
212*4882a593Smuzhiyun hdev->le_scan_window = TLV_GET_LE16(buffer);
213*4882a593Smuzhiyun break;
214*4882a593Smuzhiyun case 0x000f:
215*4882a593Smuzhiyun hdev->le_scan_int_suspend = TLV_GET_LE16(buffer);
216*4882a593Smuzhiyun break;
217*4882a593Smuzhiyun case 0x0010:
218*4882a593Smuzhiyun hdev->le_scan_window_suspend = TLV_GET_LE16(buffer);
219*4882a593Smuzhiyun break;
220*4882a593Smuzhiyun case 0x0011:
221*4882a593Smuzhiyun hdev->le_scan_int_discovery = TLV_GET_LE16(buffer);
222*4882a593Smuzhiyun break;
223*4882a593Smuzhiyun case 0x00012:
224*4882a593Smuzhiyun hdev->le_scan_window_discovery = TLV_GET_LE16(buffer);
225*4882a593Smuzhiyun break;
226*4882a593Smuzhiyun case 0x00013:
227*4882a593Smuzhiyun hdev->le_scan_int_adv_monitor = TLV_GET_LE16(buffer);
228*4882a593Smuzhiyun break;
229*4882a593Smuzhiyun case 0x00014:
230*4882a593Smuzhiyun hdev->le_scan_window_adv_monitor = TLV_GET_LE16(buffer);
231*4882a593Smuzhiyun break;
232*4882a593Smuzhiyun case 0x00015:
233*4882a593Smuzhiyun hdev->le_scan_int_connect = TLV_GET_LE16(buffer);
234*4882a593Smuzhiyun break;
235*4882a593Smuzhiyun case 0x00016:
236*4882a593Smuzhiyun hdev->le_scan_window_connect = TLV_GET_LE16(buffer);
237*4882a593Smuzhiyun break;
238*4882a593Smuzhiyun case 0x00017:
239*4882a593Smuzhiyun hdev->le_conn_min_interval = TLV_GET_LE16(buffer);
240*4882a593Smuzhiyun break;
241*4882a593Smuzhiyun case 0x00018:
242*4882a593Smuzhiyun hdev->le_conn_max_interval = TLV_GET_LE16(buffer);
243*4882a593Smuzhiyun break;
244*4882a593Smuzhiyun case 0x00019:
245*4882a593Smuzhiyun hdev->le_conn_latency = TLV_GET_LE16(buffer);
246*4882a593Smuzhiyun break;
247*4882a593Smuzhiyun case 0x0001a:
248*4882a593Smuzhiyun hdev->le_supv_timeout = TLV_GET_LE16(buffer);
249*4882a593Smuzhiyun break;
250*4882a593Smuzhiyun case 0x0001b:
251*4882a593Smuzhiyun hdev->def_le_autoconnect_timeout =
252*4882a593Smuzhiyun msecs_to_jiffies(TLV_GET_LE16(buffer));
253*4882a593Smuzhiyun break;
254*4882a593Smuzhiyun default:
255*4882a593Smuzhiyun bt_dev_warn(hdev, "unsupported parameter %u", type);
256*4882a593Smuzhiyun break;
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun buffer_left -= exp_len;
260*4882a593Smuzhiyun buffer += exp_len;
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return mgmt_cmd_complete(sk, hdev->id,
264*4882a593Smuzhiyun MGMT_OP_SET_DEF_SYSTEM_CONFIG, 0, NULL, 0);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
read_def_runtime_config(struct sock * sk,struct hci_dev * hdev,void * data,u16 data_len)267*4882a593Smuzhiyun int read_def_runtime_config(struct sock *sk, struct hci_dev *hdev, void *data,
268*4882a593Smuzhiyun u16 data_len)
269*4882a593Smuzhiyun {
270*4882a593Smuzhiyun bt_dev_dbg(hdev, "sock %p", sk);
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun return mgmt_cmd_complete(sk, hdev->id,
273*4882a593Smuzhiyun MGMT_OP_READ_DEF_RUNTIME_CONFIG, 0, NULL, 0);
274*4882a593Smuzhiyun }
275*4882a593Smuzhiyun
set_def_runtime_config(struct sock * sk,struct hci_dev * hdev,void * data,u16 data_len)276*4882a593Smuzhiyun int set_def_runtime_config(struct sock *sk, struct hci_dev *hdev, void *data,
277*4882a593Smuzhiyun u16 data_len)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun bt_dev_dbg(hdev, "sock %p", sk);
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEF_SYSTEM_CONFIG,
282*4882a593Smuzhiyun MGMT_STATUS_INVALID_PARAMS);
283*4882a593Smuzhiyun }
284