GCC Code Coverage Report


Directory: main/
File: adapter/wireless/ps.c
Date: 2025-10-04 14:03:00
Exec Total Coverage
Lines: 108 121 89.3%
Functions: 7 7 100.0%
Branches: 41 48 85.4%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2019-2025, Jacques Gagnon
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <stdio.h>
7 #include <string.h>
8 #include "zephyr/types.h"
9 #include "tools/util.h"
10 #include "bluetooth/hidp/ps.h"
11 #include "adapter/config.h"
12 #include "tests/cmds.h"
13 #include "bluetooth/mon.h"
14 #include "ps.h"
15
16 enum {
17 PS4_S = 4,
18 PS4_X,
19 PS4_C,
20 PS4_T,
21 PS4_L1,
22 PS4_R1,
23 PS4_L2,
24 PS4_R2,
25 PS4_SHARE,
26 PS4_OPTIONS,
27 PS4_L3,
28 PS4_R3,
29 PS4_PS,
30 PS4_TP,
31 PS5_MUTE,
32 };
33
34 static const uint8_t ps4_axes_idx[ADAPTER_MAX_AXES] =
35 {
36 /* AXIS_LX, AXIS_LY, AXIS_RX, AXIS_RY, TRIG_L, TRIG_R */
37 0, 1, 2, 3, 7, 8
38 };
39
40 static const uint8_t ps5_axes_idx[ADAPTER_MAX_AXES] =
41 {
42 /* AXIS_LX, AXIS_LY, AXIS_RX, AXIS_RY, TRIG_L, TRIG_R */
43 0, 1, 2, 3, 4, 5
44 };
45
46 static const struct ctrl_meta ps4_axes_meta[ADAPTER_MAX_AXES] =
47 {
48 {.neutral = 0x80, .abs_max = 0x7F, .abs_min = 0x80},
49 {.neutral = 0x80, .abs_max = 0x7F, .abs_min = 0x80, .polarity = 1},
50 {.neutral = 0x80, .abs_max = 0x7F, .abs_min = 0x80},
51 {.neutral = 0x80, .abs_max = 0x7F, .abs_min = 0x80, .polarity = 1},
52 {.neutral = 0x00, .abs_max = 0xFF, .abs_min = 0x00},
53 {.neutral = 0x00, .abs_max = 0xFF, .abs_min = 0x00},
54 };
55
56 struct hid_map {
57 union {
58 struct {
59 uint8_t reserved2[4];
60 union {
61 uint8_t hat;
62 uint32_t buttons;
63 };
64 };
65 uint8_t axes[9];
66 };
67 } __packed;
68
69 struct ps4_map {
70 uint8_t reserved[2];
71 union {
72 struct {
73 uint8_t reserved2[4];
74 union {
75 uint8_t hat;
76 uint32_t buttons;
77 };
78 };
79 uint8_t axes[9];
80 };
81 } __packed;
82
83 struct ps5_map {
84 uint8_t reserved;
85 uint8_t axes[6];
86 uint8_t reserved2;
87 union {
88 uint8_t hat;
89 uint32_t buttons;
90 };
91 } __packed;
92
93 static const uint32_t ps4_mask[4] = {0xBBFF0FFF, 0x00000000, 0x00000000, 0x00000000};
94 static const uint32_t ps4_desc[4] = {0x110000FF, 0x00000000, 0x00000000, 0x00000000};
95 static const uint32_t ps4_btns_mask[32] = {
96 0, 0, 0, 0,
97 0, 0, 0, 0,
98 0, 0, 0, 0,
99 0, 0, 0, 0,
100 BIT(PS4_S), BIT(PS4_C), BIT(PS4_X), BIT(PS4_T),
101 BIT(PS4_OPTIONS), BIT(PS4_SHARE), BIT(PS4_PS), BIT(PS4_TP),
102 0, BIT(PS4_L1), 0, BIT(PS4_L3),
103 0, BIT(PS4_R1), 0, BIT(PS4_R3),
104 };
105
106 37 static void ps4_to_generic(struct bt_data *bt_data, struct wireless_ctrl *ctrl_data) {
107 37 struct ps4_map *map = (struct ps4_map *)bt_data->base.input;
108 37 struct ctrl_meta *meta = bt_data->raw_src_mappings[PAD].meta;
109
110 37 TESTS_CMDS_LOG("\"wireless_input\": {\"report_id\": %ld, \"axes\": [%u, %u, %u, %u, %u, %u], \"btns\": %lu, \"hat\": %u},\n",
111 bt_data->base.report_id, map->axes[ps4_axes_idx[0]], map->axes[ps4_axes_idx[1]], map->axes[ps4_axes_idx[2]],
112 map->axes[ps4_axes_idx[3]], map->axes[ps4_axes_idx[4]], map->axes[ps4_axes_idx[5]], map->buttons, map->hat & 0xF);
113
114 37 memset((void *)ctrl_data, 0, sizeof(*ctrl_data));
115
116 37 ctrl_data->mask = (uint32_t *)ps4_mask;
117 37 ctrl_data->desc = (uint32_t *)ps4_desc;
118
119
2/2
✓ Branch 0 (8→5) taken 1184 times.
✓ Branch 1 (8→9) taken 37 times.
1221 for (uint32_t i = 0; i < ARRAY_SIZE(generic_btns_mask); i++) {
120
2/2
✓ Branch 0 (5→6) taken 24 times.
✓ Branch 1 (5→7) taken 1160 times.
1184 if (map->buttons & ps4_btns_mask[i]) {
121 24 ctrl_data->btns[0].value |= generic_btns_mask[i];
122 }
123 }
124
125 /* Convert hat to regular btns */
126 37 ctrl_data->btns[0].value |= hat_to_ld_btns[map->hat & 0xF];
127
128
2/2
✓ Branch 0 (10→11) taken 2 times.
✓ Branch 1 (10→21) taken 35 times.
37 if (!atomic_test_bit(&bt_data->base.flags[PAD], BT_INIT)) {
129 2 memcpy(meta, ps4_axes_meta, sizeof(ps4_axes_meta));
130 2 bt_mon_log(false, "%s: axes_cal: [", __FUNCTION__);
131
2/2
✓ Branch 0 (18→14) taken 12 times.
✓ Branch 1 (18→19) taken 2 times.
14 for (uint32_t i = 0; i < ADAPTER_MAX_AXES; i++) {
132 12 meta[i].abs_max *= MAX_PULL_BACK;
133 12 meta[i].abs_min *= MAX_PULL_BACK;
134 12 bt_data->base.axes_cal[i] = -(map->axes[ps4_axes_idx[i]] - ps4_axes_meta[i].neutral);
135
2/2
✓ Branch 0 (14→15) taken 10 times.
✓ Branch 1 (14→16) taken 2 times.
12 if (i) {
136 10 bt_mon_log(false, ", ");
137 }
138 12 bt_mon_log(false, "%d", bt_data->base.axes_cal[i]);
139 }
140 2 atomic_set_bit(&bt_data->base.flags[PAD], BT_INIT);
141 2 bt_mon_log(true, "]");
142 }
143
144
2/2
✓ Branch 0 (23→22) taken 222 times.
✓ Branch 1 (23→24) taken 37 times.
259 for (uint32_t i = 0; i < ADAPTER_MAX_AXES; i++) {
145 222 ctrl_data->axes[i].meta = &meta[i];
146 222 ctrl_data->axes[i].value = map->axes[ps4_axes_idx[i]] - ps4_axes_meta[i].neutral + bt_data->base.axes_cal[i];
147 }
148 37 }
149
150 74 static void ps5_to_generic(struct bt_data *bt_data, struct wireless_ctrl *ctrl_data) {
151 74 struct ps5_map *map = (struct ps5_map *)bt_data->base.input;
152 74 struct ctrl_meta *meta = bt_data->raw_src_mappings[PAD].meta;
153
154 74 TESTS_CMDS_LOG("\"wireless_input\": {\"report_id\": %ld, \"axes\": [%u, %u, %u, %u, %u, %u], \"btns\": %lu, \"hat\": %u},\n",
155 bt_data->base.report_id, map->axes[ps5_axes_idx[0]], map->axes[ps5_axes_idx[1]], map->axes[ps5_axes_idx[2]],
156 map->axes[ps5_axes_idx[3]], map->axes[ps5_axes_idx[4]], map->axes[ps5_axes_idx[5]], map->buttons, map->hat & 0xF);
157
158 74 memset((void *)ctrl_data, 0, sizeof(*ctrl_data));
159
160 74 ctrl_data->mask = (uint32_t *)ps4_mask;
161 74 ctrl_data->desc = (uint32_t *)ps4_desc;
162
163
2/2
✓ Branch 0 (8→5) taken 2368 times.
✓ Branch 1 (8→9) taken 74 times.
2442 for (uint32_t i = 0; i < ARRAY_SIZE(generic_btns_mask); i++) {
164
2/2
✓ Branch 0 (5→6) taken 48 times.
✓ Branch 1 (5→7) taken 2320 times.
2368 if (map->buttons & ps4_btns_mask[i]) {
165 48 ctrl_data->btns[0].value |= generic_btns_mask[i];
166 }
167 }
168
169 /* Convert hat to regular btns */
170 74 ctrl_data->btns[0].value |= hat_to_ld_btns[map->hat & 0xF];
171
172
2/2
✓ Branch 0 (10→11) taken 4 times.
✓ Branch 1 (10→16) taken 70 times.
74 if (!atomic_test_bit(&bt_data->base.flags[PAD], BT_INIT)) {
173 4 memcpy(meta, ps4_axes_meta, sizeof(ps4_axes_meta));
174
2/2
✓ Branch 0 (14→13) taken 24 times.
✓ Branch 1 (14→15) taken 4 times.
28 for (uint32_t i = 0; i < ADAPTER_MAX_AXES; i++) {
175 24 meta[i].abs_max *= MAX_PULL_BACK;
176 24 meta[i].abs_min *= MAX_PULL_BACK;
177 24 bt_data->base.axes_cal[i] = -(map->axes[ps5_axes_idx[i]] - ps4_axes_meta[i].neutral);
178 }
179 4 atomic_set_bit(&bt_data->base.flags[PAD], BT_INIT);
180 }
181
182
2/2
✓ Branch 0 (18→17) taken 444 times.
✓ Branch 1 (18→19) taken 74 times.
518 for (uint32_t i = 0; i < ADAPTER_MAX_AXES; i++) {
183 444 ctrl_data->axes[i].meta = &meta[i];
184 444 ctrl_data->axes[i].value = map->axes[ps5_axes_idx[i]] - ps4_axes_meta[i].neutral + bt_data->base.axes_cal[i];
185 }
186 74 }
187
188 111 static void hid_to_generic(struct bt_data *bt_data, struct wireless_ctrl *ctrl_data) {
189 111 struct hid_map *map = (struct hid_map *)bt_data->base.input;
190 111 struct ctrl_meta *meta = bt_data->raw_src_mappings[PAD].meta;
191
192 111 TESTS_CMDS_LOG("\"wireless_input\": {\"report_id\": %ld, \"axes\": [%u, %u, %u, %u, %u, %u], \"btns\": %lu, \"hat\": %u},\n",
193 bt_data->base.report_id, map->axes[ps4_axes_idx[0]], map->axes[ps4_axes_idx[1]], map->axes[ps4_axes_idx[2]],
194 map->axes[ps4_axes_idx[3]], map->axes[ps4_axes_idx[4]], map->axes[ps4_axes_idx[5]], map->buttons, map->hat & 0xF);
195
196 111 memset((void *)ctrl_data, 0, sizeof(*ctrl_data));
197
198 111 ctrl_data->mask = (uint32_t *)ps4_mask;
199 111 ctrl_data->desc = (uint32_t *)ps4_desc;
200
201
2/2
✓ Branch 0 (8→5) taken 3552 times.
✓ Branch 1 (8→9) taken 111 times.
3663 for (uint32_t i = 0; i < ARRAY_SIZE(generic_btns_mask); i++) {
202
2/2
✓ Branch 0 (5→6) taken 72 times.
✓ Branch 1 (5→7) taken 3480 times.
3552 if (map->buttons & ps4_btns_mask[i]) {
203 72 ctrl_data->btns[0].value |= generic_btns_mask[i];
204 }
205 }
206
207 /* Convert hat to regular btns */
208 111 ctrl_data->btns[0].value |= hat_to_ld_btns[map->hat & 0xF];
209
210
2/2
✓ Branch 0 (10→11) taken 6 times.
✓ Branch 1 (10→16) taken 105 times.
111 if (!atomic_test_bit(&bt_data->base.flags[PAD], BT_INIT)) {
211 6 memcpy(meta, ps4_axes_meta, sizeof(ps4_axes_meta));
212
2/2
✓ Branch 0 (14→13) taken 36 times.
✓ Branch 1 (14→15) taken 6 times.
42 for (uint32_t i = 0; i < ADAPTER_MAX_AXES; i++) {
213 36 meta[i].abs_max *= MAX_PULL_BACK;
214 36 meta[i].abs_min *= MAX_PULL_BACK;
215 36 bt_data->base.axes_cal[i] = -(map->axes[ps4_axes_idx[i]] - ps4_axes_meta[i].neutral);
216 }
217 6 atomic_set_bit(&bt_data->base.flags[PAD], BT_INIT);
218 }
219
220
2/2
✓ Branch 0 (18→17) taken 666 times.
✓ Branch 1 (18→19) taken 111 times.
777 for (uint32_t i = 0; i < ADAPTER_MAX_AXES; i++) {
221 666 ctrl_data->axes[i].meta = &meta[i];
222 666 ctrl_data->axes[i].value = map->axes[ps4_axes_idx[i]] - ps4_axes_meta[i].neutral + bt_data->base.axes_cal[i];
223 }
224 111 }
225
226 2 static void ps4_fb_from_generic(struct generic_fb *fb_data, struct bt_data *bt_data) {
227 2 struct bt_hidp_ps4_set_conf *set_conf = (struct bt_hidp_ps4_set_conf *)bt_data->base.output;
228
229
1/3
✓ Branch 0 (2→3) taken 2 times.
✗ Branch 1 (2→6) not taken.
✗ Branch 2 (2→7) not taken.
2 switch (fb_data->type) {
230 2 case FB_TYPE_RUMBLE:
231
1/2
✓ Branch 0 (3→4) taken 2 times.
✗ Branch 1 (3→5) not taken.
2 if (fb_data->state) {
232 2 set_conf->hf_motor_pwr = fb_data->hf_pwr;
233 2 set_conf->lf_motor_pwr = fb_data->lf_pwr;
234 }
235 else {
236 set_conf->hf_motor_pwr = 0x00;
237 set_conf->lf_motor_pwr = 0x00;
238 }
239 break;
240 case FB_TYPE_PLAYER_LED:
241 set_conf->leds = hw_config.ps_ctrl_colors[bt_data->base.pids->out_idx];
242 break;
243 }
244 2 }
245
246 1 static void ps5_fb_from_generic(struct generic_fb *fb_data, struct bt_data *bt_data) {
247 1 struct bt_hidp_ps5_set_conf *set_conf = (struct bt_hidp_ps5_set_conf *)bt_data->base.output;
248
249
1/3
✓ Branch 0 (2→3) taken 1 times.
✗ Branch 1 (2→6) not taken.
✗ Branch 2 (2→7) not taken.
1 switch (fb_data->type) {
250 1 case FB_TYPE_RUMBLE:
251
1/2
✓ Branch 0 (3→4) taken 1 times.
✗ Branch 1 (3→5) not taken.
1 if (fb_data->state) {
252 1 set_conf->hf_motor_pwr = fb_data->hf_pwr;
253 1 set_conf->lf_motor_pwr = fb_data->lf_pwr;
254 }
255 else {
256 set_conf->hf_motor_pwr = 0x00;
257 set_conf->lf_motor_pwr = 0x00;
258 }
259 break;
260 case FB_TYPE_PLAYER_LED:
261 set_conf->leds = hw_config.ps_ctrl_colors[bt_data->base.pids->out_idx];
262 break;
263 }
264 1 }
265
266 222 int32_t ps_to_generic(struct bt_data *bt_data, struct wireless_ctrl *ctrl_data) {
267
3/4
✓ Branch 0 (2→3) taken 111 times.
✓ Branch 1 (2→5) taken 37 times.
✓ Branch 2 (2→8) taken 74 times.
✗ Branch 3 (2→10) not taken.
222 switch (bt_data->base.report_id) {
268 111 case 0x01:
269 111 hid_to_generic(bt_data, ctrl_data);
270 111 break;
271 37 case 0x11:
272 37 ps4_to_generic(bt_data, ctrl_data);
273 37 break;
274 74 case 0x31:
275 74 ps5_to_generic(bt_data, ctrl_data);
276 74 break;
277 default:
278 printf("# Unknown report type: %02lX\n", bt_data->base.report_type);
279 return -1;
280 }
281
282 return 0;
283 }
284
285 3 bool ps_fb_from_generic(struct generic_fb *fb_data, struct bt_data *bt_data) {
286 3 bool ret = true;
287
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→5) taken 2 times.
3 switch (bt_data->base.pids->subtype) {
288 1 case BT_PS5_DS:
289 1 ps5_fb_from_generic(fb_data, bt_data);
290 1 break;
291 2 default:
292 2 ps4_fb_from_generic(fb_data, bt_data);
293 2 break;
294 }
295 3 return ret;
296 }
297