1 // Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <string.h>
16 #include "esp_log.h"
17 #include "esp_err.h"
18 #include "esp_hid_common.h"
19 #if (CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE)
20 #include "esp_gatt_defs.h"
21 #endif
22 static const char *TAG = "hid_parser";
23
24 typedef struct {
25 uint16_t appearance;
26 uint8_t usage_mask;
27 uint8_t reports_len;
28 esp_hid_report_item_t reports[64];
29 } temp_hid_report_map_t;
30
31 typedef struct {
32 uint8_t cmd;
33 uint8_t len;
34 union {
35 uint32_t value;
36 uint8_t data[4];
37 };
38 } hid_report_cmd_t;
39
40 typedef struct {
41 uint16_t usage_page;
42 uint16_t usage;
43 uint16_t inner_usage_page;
44 uint16_t inner_usage;
45 uint8_t report_id;
46 uint16_t input_len;
47 uint16_t output_len;
48 uint16_t feature_len;
49 } hid_report_params_t;
50
51 typedef enum {
52 PARSE_WAIT_USAGE_PAGE, PARSE_WAIT_USAGE, PARSE_WAIT_COLLECTION_APPLICATION, PARSE_WAIT_END_COLLECTION
53 } s_parse_step_t;
54
55
56 static s_parse_step_t s_parse_step = PARSE_WAIT_USAGE_PAGE;
57 static uint8_t s_collection_depth = 0;
58 static hid_report_params_t s_report_params = {0,};
59 static uint16_t s_report_size = 0;
60 static uint16_t s_report_count = 0;
61
62 static bool s_new_map = false;
63 static temp_hid_report_map_t *s_temp_hid_report_map;
64
add_report(temp_hid_report_map_t * map,esp_hid_report_item_t * item)65 static int add_report(temp_hid_report_map_t *map, esp_hid_report_item_t *item)
66 {
67 if (map->reports_len >= 64) {
68 ESP_LOGE(TAG, "reports overflow");
69 return -1;
70 }
71 memcpy(&(map->reports[map->reports_len]), item, sizeof(esp_hid_report_item_t));
72 map->reports_len++;
73 return 0;
74 }
75
handle_report(hid_report_params_t * report,bool first)76 static int handle_report(hid_report_params_t *report, bool first)
77 {
78 if (s_temp_hid_report_map == NULL) {
79 s_temp_hid_report_map = (temp_hid_report_map_t *)calloc(1, sizeof(temp_hid_report_map_t));
80 if (s_temp_hid_report_map == NULL) {
81 ESP_LOGE(TAG, "malloc failed");
82 return -1;
83 }
84 }
85 temp_hid_report_map_t *map = s_temp_hid_report_map;
86 if (first) {
87 memset(map, 0, sizeof(temp_hid_report_map_t));
88 }
89
90 if (report->usage_page == HID_USAGE_PAGE_GENERIC_DESKTOP && report->usage == HID_USAGE_KEYBOARD) {
91 //Keyboard
92 map->usage_mask |= ESP_HID_USAGE_KEYBOARD;
93 if (report->input_len > 0) {
94 esp_hid_report_item_t item = {
95 .usage = ESP_HID_USAGE_KEYBOARD,
96 .report_id = report->report_id,
97 .report_type = ESP_HID_REPORT_TYPE_INPUT,
98 .protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
99 .value_len = report->input_len / 8,
100 };
101 if (add_report(map, &item) != 0) {
102 return -1;
103 }
104
105 item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
106 item.value_len = 8;
107 if (add_report(map, &item) != 0) {
108 return -1;
109 }
110 }
111 if (report->output_len > 0) {
112 esp_hid_report_item_t item = {
113 .usage = ESP_HID_USAGE_KEYBOARD,
114 .report_id = report->report_id,
115 .report_type = ESP_HID_REPORT_TYPE_OUTPUT,
116 .protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
117 .value_len = report->output_len / 8,
118 };
119 if (add_report(map, &item) != 0) {
120 return -1;
121 }
122
123 item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
124 item.value_len = 1;
125 if (add_report(map, &item) != 0) {
126 return -1;
127 }
128 }
129 } else if (report->usage_page == HID_USAGE_PAGE_GENERIC_DESKTOP && report->usage == HID_USAGE_MOUSE) {
130 //Mouse
131 map->usage_mask |= ESP_HID_USAGE_MOUSE;
132 if (report->input_len > 0) {
133 esp_hid_report_item_t item = {
134 .usage = ESP_HID_USAGE_MOUSE,
135 .report_id = report->report_id,
136 .report_type = ESP_HID_REPORT_TYPE_INPUT,
137 .protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
138 .value_len = report->input_len / 8,
139 };
140 if (add_report(map, &item) != 0) {
141 return -1;
142 }
143
144 item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
145 item.value_len = 3;
146 if (add_report(map, &item) != 0) {
147 return -1;
148 }
149 }
150 } else {
151 esp_hid_usage_t cusage = ESP_HID_USAGE_GENERIC;
152 if (report->usage_page == HID_USAGE_PAGE_GENERIC_DESKTOP) {
153 if (report->usage == HID_USAGE_JOYSTICK) {
154 //Joystick
155 map->usage_mask |= ESP_HID_USAGE_JOYSTICK;
156 cusage = ESP_HID_USAGE_JOYSTICK;
157 } else if (report->usage == HID_USAGE_GAMEPAD) {
158 //Gamepad
159 map->usage_mask |= ESP_HID_USAGE_GAMEPAD;
160 cusage = ESP_HID_USAGE_GAMEPAD;
161 }
162 } else if (report->usage_page == HID_USAGE_PAGE_CONSUMER_DEVICE && report->usage == HID_USAGE_CONSUMER_CONTROL) {
163 //Consumer Control
164 map->usage_mask |= ESP_HID_USAGE_CCONTROL;
165 cusage = ESP_HID_USAGE_CCONTROL;
166 } else if (report->usage_page >= 0xFF) {
167 //Vendor
168 map->usage_mask |= ESP_HID_USAGE_VENDOR;
169 cusage = ESP_HID_USAGE_VENDOR;
170 }
171 //Generic
172 esp_hid_report_item_t item = {
173 .usage = cusage,
174 .report_id = report->report_id,
175 .report_type = ESP_HID_REPORT_TYPE_INPUT,
176 .protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
177 .value_len = report->input_len / 8,
178 };
179 if (report->input_len > 0) {
180 if (add_report(map, &item) != 0) {
181 return -1;
182 }
183 }
184 if (report->output_len > 0) {
185 item.report_type = ESP_HID_REPORT_TYPE_OUTPUT;
186 item.value_len = report->output_len / 8;
187 if (add_report(map, &item) != 0) {
188 return -1;
189 }
190 }
191 if (report->feature_len > 0) {
192 item.report_type = ESP_HID_REPORT_TYPE_FEATURE;
193 item.value_len = report->feature_len / 8;
194 if (add_report(map, &item) != 0) {
195 return -1;
196 }
197 }
198 }
199 return 0;
200 }
201
202
parse_cmd(const uint8_t * data,size_t len,size_t index,hid_report_cmd_t ** out)203 static int parse_cmd(const uint8_t *data, size_t len, size_t index, hid_report_cmd_t **out)
204 {
205 if (index == len) {
206 return 0;
207 }
208 hid_report_cmd_t *cmd = (hid_report_cmd_t *)malloc(sizeof(hid_report_cmd_t));
209 if (cmd == NULL) {
210 return -1;
211 }
212 const uint8_t *dp = data + index;
213 cmd->cmd = *dp & 0xFC;
214 cmd->len = *dp & 0x03;
215 cmd->value = 0;
216 if (cmd->len == 3) {
217 cmd->len = 4;
218 }
219 if ((len - index - 1) < cmd->len) {
220 ESP_LOGE(TAG, "not enough bytes! cmd: 0x%02x, len: %u, index: %u", cmd->cmd, cmd->len, index);
221 free(cmd);
222 return -1;
223 }
224 memcpy(cmd->data, dp + 1, cmd->len);
225 *out = cmd;
226 return cmd->len + 1;
227 }
228
handle_cmd(hid_report_cmd_t * cmd)229 static int handle_cmd(hid_report_cmd_t *cmd)
230 {
231 switch (s_parse_step) {
232 case PARSE_WAIT_USAGE_PAGE: {
233 if (cmd->cmd != HID_RM_USAGE_PAGE) {
234 ESP_LOGE(TAG, "expected USAGE_PAGE, but got 0x%02x", cmd->cmd);
235 return -1;
236 }
237 s_report_size = 0;
238 s_report_count = 0;
239 memset(&s_report_params, 0, sizeof(hid_report_params_t));
240 s_report_params.usage_page = cmd->value;
241 s_parse_step = PARSE_WAIT_USAGE;
242 break;
243 }
244 case PARSE_WAIT_USAGE: {
245 if (cmd->cmd != HID_RM_USAGE) {
246 ESP_LOGE(TAG, "expected USAGE, but got 0x%02x", cmd->cmd);
247 s_parse_step = PARSE_WAIT_USAGE_PAGE;
248 return -1;
249 }
250 s_report_params.usage = cmd->value;
251 s_parse_step = PARSE_WAIT_COLLECTION_APPLICATION;
252 break;
253 }
254 case PARSE_WAIT_COLLECTION_APPLICATION: {
255 if (cmd->cmd != HID_RM_COLLECTION) {
256 ESP_LOGE(TAG, "expected COLLECTION, but got 0x%02x", cmd->cmd);
257 s_parse_step = PARSE_WAIT_USAGE_PAGE;
258 return -1;
259 }
260 if (cmd->value != 1) {
261 ESP_LOGE(TAG, "expected APPLICATION, but got 0x%02x", cmd->value);
262 s_parse_step = PARSE_WAIT_USAGE_PAGE;
263 return -1;
264 }
265 s_report_params.report_id = 0;
266 s_collection_depth = 1;
267 s_parse_step = PARSE_WAIT_END_COLLECTION;
268 break;
269 }
270 case PARSE_WAIT_END_COLLECTION: {
271 if (cmd->cmd == HID_RM_REPORT_ID) {
272 if (s_report_params.report_id && s_report_params.report_id != cmd->value) {
273 //report id changed mid collection
274 if (s_report_params.input_len & 0x7) {
275 ESP_LOGE(TAG, "ERROR: INPUT report does not amount to full bytes! %d (%d)", s_report_params.input_len, s_report_params.input_len & 0x7);
276 } else if (s_report_params.output_len & 0x7) {
277 ESP_LOGE(TAG, "ERROR: OUTPUT report does not amount to full bytes! %d (%d)", s_report_params.output_len, s_report_params.output_len & 0x7);
278 } else if (s_report_params.feature_len & 0x7) {
279 ESP_LOGE(TAG, "ERROR: FEATURE report does not amount to full bytes! %d (%d)", s_report_params.feature_len, s_report_params.feature_len & 0x7);
280 } else {
281 //SUCCESS!!!
282 int res = handle_report(&s_report_params, s_new_map);
283 if (res != 0) {
284 s_parse_step = PARSE_WAIT_USAGE_PAGE;
285 return -1;
286 }
287 s_new_map = false;
288
289 s_report_params.input_len = 0;
290 s_report_params.output_len = 0;
291 s_report_params.feature_len = 0;
292 s_report_params.usage = s_report_params.inner_usage;
293 s_report_params.usage_page = s_report_params.inner_usage_page;
294 }
295 }
296 s_report_params.report_id = cmd->value;
297 } else if (cmd->cmd == HID_RM_USAGE_PAGE) {
298 s_report_params.inner_usage_page = cmd->value;
299 } else if (cmd->cmd == HID_RM_USAGE) {
300 s_report_params.inner_usage = cmd->value;
301 } else if (cmd->cmd == HID_RM_REPORT_SIZE) {
302 s_report_size = cmd->value;
303 } else if (cmd->cmd == HID_RM_REPORT_COUNT) {
304 s_report_count = cmd->value;
305 } else if (cmd->cmd == HID_RM_INPUT) {
306 s_report_params.input_len += (s_report_size * s_report_count);
307 } else if (cmd->cmd == HID_RM_OUTPUT) {
308 s_report_params.output_len += (s_report_size * s_report_count);
309 } else if (cmd->cmd == HID_RM_FEATURE) {
310 s_report_params.feature_len += (s_report_size * s_report_count);
311 } else if (cmd->cmd == HID_RM_COLLECTION) {
312 s_collection_depth += 1;
313 } else if (cmd->cmd == HID_RM_END_COLLECTION) {
314 s_collection_depth -= 1;
315 if (s_collection_depth == 0) {
316 if (s_report_params.input_len & 0x7) {
317 ESP_LOGE(TAG, "ERROR: INPUT report does not amount to full bytes! %d (%d)", s_report_params.input_len, s_report_params.input_len & 0x7);
318 } else if (s_report_params.output_len & 0x7) {
319 ESP_LOGE(TAG, "ERROR: OUTPUT report does not amount to full bytes! %d (%d)", s_report_params.output_len, s_report_params.output_len & 0x7);
320 } else if (s_report_params.feature_len & 0x7) {
321 ESP_LOGE(TAG, "ERROR: FEATURE report does not amount to full bytes! %d (%d)", s_report_params.feature_len, s_report_params.feature_len & 0x7);
322 } else {
323 //SUCCESS!!!
324 int res = handle_report(&s_report_params, s_new_map);
325 if (res != 0) {
326 s_parse_step = PARSE_WAIT_USAGE_PAGE;
327 return -1;
328 }
329 s_new_map = false;
330 }
331 s_parse_step = PARSE_WAIT_USAGE_PAGE;
332 }
333 }
334
335 break;
336 }
337 default:
338 s_parse_step = PARSE_WAIT_USAGE_PAGE;
339 break;
340 }
341 return 0;
342 }
343
344
esp_hid_parse_report_map(const uint8_t * hid_rm,size_t hid_rm_len)345 esp_hid_report_map_t *esp_hid_parse_report_map(const uint8_t *hid_rm, size_t hid_rm_len)
346 {
347 size_t index = 0;
348 int res;
349 s_new_map = true;
350
351 while (index < hid_rm_len) {
352 hid_report_cmd_t *cmd;
353 res = parse_cmd(hid_rm, hid_rm_len, index, &cmd);
354 if (res < 0) {
355 ESP_LOGE(TAG, "Failed parsing the descriptor at index: %u", index);
356 return NULL;
357 }
358 index += res;
359 res = handle_cmd(cmd);
360 free(cmd);
361 if (res != 0) {
362 return NULL;
363 }
364 }
365
366 esp_hid_report_map_t *out = (esp_hid_report_map_t *)calloc(1, sizeof(esp_hid_report_map_t));
367 if (out == NULL) {
368 ESP_LOGE(TAG, "hid_report_map malloc failed");
369 free(s_temp_hid_report_map);
370 s_temp_hid_report_map = NULL;
371 return NULL;
372 }
373
374 temp_hid_report_map_t *map = s_temp_hid_report_map;
375
376 esp_hid_report_item_t *reports = (esp_hid_report_item_t *)calloc(1, map->reports_len * sizeof(esp_hid_report_item_t));
377 if (reports == NULL) {
378 ESP_LOGE(TAG, "hid_report_items malloc failed! %u maps", map->reports_len);
379 free(out);
380 free(s_temp_hid_report_map);
381 s_temp_hid_report_map = NULL;
382 return NULL;
383 }
384
385 if (map->usage_mask & ESP_HID_USAGE_KEYBOARD) {
386 out->usage = ESP_HID_USAGE_KEYBOARD;
387 out->appearance = ESP_HID_APPEARANCE_KEYBOARD;
388 } else if (map->usage_mask & ESP_HID_USAGE_MOUSE) {
389 out->usage = ESP_HID_USAGE_MOUSE;
390 out->appearance = ESP_HID_APPEARANCE_MOUSE;
391 } else if (map->usage_mask & ESP_HID_USAGE_JOYSTICK) {
392 out->usage = ESP_HID_USAGE_JOYSTICK;
393 out->appearance = ESP_HID_APPEARANCE_JOYSTICK;
394 } else if (map->usage_mask & ESP_HID_USAGE_GAMEPAD) {
395 out->usage = ESP_HID_USAGE_GAMEPAD;
396 out->appearance = ESP_HID_APPEARANCE_GAMEPAD;
397 } else if (map->usage_mask & ESP_HID_USAGE_CCONTROL) {
398 out->usage = ESP_HID_USAGE_CCONTROL;
399 out->appearance = ESP_HID_APPEARANCE_KEYBOARD;
400 } else {
401 out->usage = ESP_HID_USAGE_GENERIC;
402 out->appearance = ESP_HID_APPEARANCE_GENERIC;
403 }
404 out->reports_len = map->reports_len;
405 memcpy(reports, map->reports, map->reports_len * sizeof(esp_hid_report_item_t));
406 out->reports = reports;
407 free(s_temp_hid_report_map);
408 s_temp_hid_report_map = NULL;
409
410 return out;
411 }
412
esp_hid_free_report_map(esp_hid_report_map_t * map)413 void esp_hid_free_report_map(esp_hid_report_map_t *map)
414 {
415 if (map != NULL){
416 free(map->reports);
417 free(map);
418 }
419 }
420
esp_hid_usage_from_appearance(uint16_t appearance)421 esp_hid_usage_t esp_hid_usage_from_appearance(uint16_t appearance)
422 {
423 return ESP_HID_USAGE_GENERIC;
424 }
425
esp_hid_usage_from_cod(uint32_t cod)426 esp_hid_usage_t esp_hid_usage_from_cod(uint32_t cod)
427 {
428 return ESP_HID_USAGE_GENERIC;
429 }
430
431 static const char *s_unknown_str = "UNKNOWN";
432 static const char *s_hid_protocol_names[] = {"BOOT", "REPORT"};
433 static const char *s_hid_report_type_names[] = {"NULL", "INPUT", "OUTPUT", "FEATURE"};
434 static const char *s_hid_cod_major_names[] = {"MISC", "COMPUTER", "PHONE", "LAN_NAP", "AV", "PERIPHERAL", "IMAGING", "WEARABLE", "TOY", "HEALTH"};
435 static const char *s_hid_cod_minor_names[7] = {"GENERIC", "JOYSTICK", "GAMEPAD", "REMOTE", "SENSOR", "TABLET", "CARD_READER"};
436
esp_hid_usage_str(esp_hid_usage_t usage)437 const char *esp_hid_usage_str(esp_hid_usage_t usage)
438 {
439 switch (usage) {
440 case ESP_HID_USAGE_GENERIC: return "GENERIC";
441 case ESP_HID_USAGE_KEYBOARD: return "KEYBOARD";
442 case ESP_HID_USAGE_MOUSE: return "MOUSE";
443 case ESP_HID_USAGE_JOYSTICK: return "JOYSTICK";
444 case ESP_HID_USAGE_GAMEPAD: return "GAMEPAD";
445 case ESP_HID_USAGE_CCONTROL: return "CCONTROL";
446 case ESP_HID_USAGE_VENDOR: return "VENDOR";
447 default: break;
448 }
449 return s_unknown_str;
450 }
451
esp_hid_protocol_mode_str(uint8_t protocol)452 const char *esp_hid_protocol_mode_str(uint8_t protocol)
453 {
454 if (protocol >= (sizeof(s_hid_protocol_names)/sizeof(s_hid_protocol_names[0]))) {
455 return s_unknown_str;
456 }
457 return s_hid_protocol_names[protocol];
458 }
459
esp_hid_report_type_str(uint8_t report_type)460 const char *esp_hid_report_type_str(uint8_t report_type)
461 {
462 if (report_type >= (sizeof(s_hid_report_type_names)/sizeof(s_hid_report_type_names[0]))) {
463 return s_unknown_str;
464 }
465 return s_hid_report_type_names[report_type];
466 }
467
esp_hid_cod_major_str(uint8_t cod_major)468 const char *esp_hid_cod_major_str(uint8_t cod_major)
469 {
470 if (cod_major >= (sizeof(s_hid_cod_major_names)/sizeof(s_hid_cod_major_names[0]))) {
471 return s_unknown_str;
472 }
473 return s_hid_cod_major_names[cod_major];
474 }
475
esp_hid_cod_minor_print(uint8_t cod_min,FILE * fp)476 void esp_hid_cod_minor_print(uint8_t cod_min, FILE *fp)
477 {
478 if (cod_min & ESP_HID_COD_MIN_KEYBOARD) {
479 fputs("KEYBOARD", fp);
480 }
481 if (cod_min & ESP_HID_COD_MIN_MOUSE) {
482 if (cod_min & ESP_HID_COD_MIN_KEYBOARD) {
483 fputs("+", fp);
484 }
485 fputs("MOUSE", fp);
486 }
487 if (cod_min & 0xF0) {
488 if (cod_min & 0x0F) {
489 fputs("+", fp);
490 } else {
491 return;
492 }
493 }
494 cod_min &= 0x0F;
495 if (cod_min < ESP_HID_COD_MIN_MAX) {
496 fprintf(fp, "%s", s_hid_cod_minor_names[cod_min]);
497 }
498 }
499
esp_hid_disconnect_reason_str(esp_hid_transport_t transport,int reason)500 const char *esp_hid_disconnect_reason_str(esp_hid_transport_t transport, int reason)
501 {
502 if (transport == ESP_HID_TRANSPORT_BLE) {
503 #if (CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE)
504 switch ((esp_gatt_conn_reason_t)reason) {
505 case ESP_GATT_CONN_L2C_FAILURE: return "L2C_FAILURE";
506 case ESP_GATT_CONN_TIMEOUT: return "TIMEOUT";
507 case ESP_GATT_CONN_TERMINATE_PEER_USER: return "TERMINATE_PEER_USER";
508 case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: return "TERMINATE_LOCAL_HOST";
509 case ESP_GATT_CONN_LMP_TIMEOUT: return "LMP_TIMEOUT";
510 case ESP_GATT_CONN_FAIL_ESTABLISH: return "FAIL_ESTABLISH";
511 case ESP_GATT_CONN_CONN_CANCEL: return "CONN_CANCEL";
512 case ESP_GATT_CONN_NONE: return "NONE";
513 default: break;
514 }
515 #endif /* CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE */
516 }
517 return s_unknown_str;
518 }
519