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