diff --git a/examples/USB_Host_Keyboard_Mouse/USB_Host_Keyboard_Mouse.ino b/examples/USB_Host_Keyboard_Mouse/USB_Host_Keyboard_Mouse.ino new file mode 100644 index 0000000..3d96e7a --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/USB_Host_Keyboard_Mouse.ino @@ -0,0 +1,178 @@ +/** + * @file USB_Host_Keyboard_Mouse.ino + * @author Lewis He (lewishe@outlook.com) + * @license MIT + * @copyright Copyright (c) 2024 Shenzhen Xin Yuan Electronic Technology Co., Ltd + * @date 2024-11-26 + * @note + * * 1. After the sketch is uploaded, the ESP32S3 port will disappear and enter the host mode. + * * At this time, you need to connect the mouse through USB-C to USB-A, + * * and then press the BOOT button to turn on the OTG power supply state. + * * When the mouse is detected, the screen mouse will become operational. + * * 2. When the program needs to be re-burned, T-Display-AMOLED must be manually + * * entered into the download mode so that the port can be generated correctly + * * and the sketch can be uploaded. + * * ** Steps to manually enter download mode ** + * * - Connect the board via the USB cable + * * - Press and hold the BOOT button , While still pressing the BOOT button, press RST + * * - Release the RST + * * - Release the BOOT button + * * - Upload sketch + * * 3. Only T4-S3 / AMOLED-1.91-PLUS has OTG function , + * * Other devices require additional power supply for keyboard and mouse + * * + * * Youtube : https://youtu.be/cq4Wg4ffzKM + */ + +#include +#include "usb_hid.h" +#include +#include + +using namespace ace_button; +AceButton button; + +// Mouse pointer image +LV_IMG_DECLARE(image_emoji); + +LilyGo_Class amoled; + +// Input device parameters +struct InputParams inputParams; + + +// The event handler for the button. +void handleEvent(AceButton *button, uint8_t eventType, uint8_t buttonState) +{ + // Print out a message for all events. + Serial.print(F("handleEvent(): eventType: ")); + Serial.print(AceButton::eventName(eventType)); + Serial.print(F("; buttonState: ")); + Serial.println(buttonState); + + uint8_t id = button->getId(); + if (eventType != AceButton::kEventClicked) { + return; + } + + // Only T4-S3 / AMOLED-1.91-PLUS has OTG function + if (amoled.hasOTG()) { + if (amoled.isEnableOTG()) { + amoled.disableOTG(); + Serial.println("disableOTG"); + } else { + amoled.enableOTG(); + amoled.enableCharge(); + Serial.println("enableOTG"); + } + } +} + + + +void setup() +{ + Serial.begin(115200); + + Serial.println("USB_Host_Keyboard_Mouse example"); + + + bool rslt = false; + + // Begin LilyGo 1.47 Inch AMOLED board class + //rslt = amoled.beginAMOLED_147(); + + + // Begin LilyGo 1.91 Inch AMOLED board class + //rslt = amoled.beginAMOLED_191(); + + // Begin LilyGo 2.41 Inch AMOLED board class + //rslt = amoled.beginAMOLED_241(); + + // Automatically determine the access device + rslt = amoled.begin(); + + if (!rslt) { + while (1) { + Serial.println("The board model cannot be detected, please raise the Core Debug Level to an error"); + delay(1000); + } + } + + + // initialize the corresponding AceButton + uint8_t pin = 0; //BOOT Pin + pinMode(pin, INPUT_PULLUP); + button.init(pin, HIGH); + // Configure the ButtonConfig with the event handler, and enable all higher + // level events. + ButtonConfig *buttonConfig = ButtonConfig::getSystemButtonConfig(); + buttonConfig->setEventHandler(handleEvent); + buttonConfig->setFeature(ButtonConfig::kFeatureClick); + + // Register lvgl helper + beginLvglHelper(amoled); + + // Register USB input device + inputParams.queue = xQueueCreate( 10, sizeof( struct InputData ) ); //Creating an Input Queue + inputParams.icon = (const void *)&image_emoji; //Set mouse pointer icon + beginLvglInputDevice(inputParams); //Register lvgl to allow input device input + + + setupUSB(inputParams.queue); // Initialize USB Host + + // Creating an Input Box + lv_obj_t *radio_ta = lv_textarea_create(lv_scr_act()); + lv_obj_set_size(radio_ta, LV_PCT(80), LV_PCT(20)); + lv_textarea_set_text(radio_ta, ""); + lv_textarea_set_max_length(radio_ta, 1024); + lv_obj_align(radio_ta, LV_ALIGN_TOP_MID, 0, 20); + + + lv_obj_t *label1 = lv_label_create(lv_scr_act()); + lv_label_set_long_mode(label1, LV_LABEL_LONG_WRAP); /*Break the long lines*/ + lv_label_set_recolor(label1, true); /*Enable re-coloring by commands in the text*/ + lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label, align the lines to the center " + "and wrap long text automatically."); + lv_obj_set_width(label1, 150); /*Set smaller width to make the lines wrap*/ + lv_obj_set_style_text_align(label1, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_align(label1, LV_ALIGN_CENTER, 0, -40); + + lv_obj_t *label2 = lv_label_create(lv_scr_act()); + lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR); /*Circular scroll*/ + lv_obj_set_width(label2, 150); + lv_label_set_text(label2, "It is a circularly scrolling text. "); + lv_obj_align(label2, LV_ALIGN_CENTER, 0, 20); + + + + lv_obj_t *btn1 = lv_btn_create(lv_scr_act()); + lv_obj_align(btn1, LV_ALIGN_BOTTOM_MID, -80, -40); + lv_obj_t *label = lv_label_create(btn1); + lv_label_set_text(label, "Button"); + lv_obj_center(label); + + lv_obj_t *btn2 = lv_btn_create(lv_scr_act()); + lv_obj_align(btn2, LV_ALIGN_BOTTOM_MID, 80, -40); + lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE); + lv_obj_set_height(btn2, LV_SIZE_CONTENT); + label = lv_label_create(btn2); + lv_label_set_text(label, "Toggle"); + lv_obj_center(label); + +} + + +void loop() +{ + + button.check(); + lv_task_handler(); + delay(2); +} + + + + + + diff --git a/examples/USB_Host_Keyboard_Mouse/image_emoji.c b/examples/USB_Host_Keyboard_Mouse/image_emoji.c new file mode 100644 index 0000000..ebef382 --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/image_emoji.c @@ -0,0 +1,174 @@ +#ifdef __has_include + #if __has_include("lvgl.h") + #ifndef LV_LVGL_H_INCLUDE_SIMPLE + #define LV_LVGL_H_INCLUDE_SIMPLE + #endif + #endif +#endif + +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) + #include "lvgl.h" +#else + #include "lvgl/lvgl.h" +#endif + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_IMAGE_EMOJI +#define LV_ATTRIBUTE_IMG_IMAGE_EMOJI +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_IMAGE_EMOJI uint8_t image_emoji_map[] = { +#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8 + /*Pixel format: Alpha 8 bit, Red: 3 bit, Green: 3 bit, Blue: 2 bit*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x1c, 0xfd, 0x88, 0xfd, 0xcb, 0xfd, 0xef, 0xfd, 0xf9, 0xfd, 0xfe, 0xfd, 0xff, 0xfd, 0xfc, 0xfd, 0xf5, 0xfd, 0xdb, 0xfd, 0x8a, 0xfd, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x43, 0xfd, 0xc7, 0xfd, 0xfc, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xd9, 0xfd, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x9b, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xd7, 0xfd, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xb0, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xfb, 0xfd, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xdf, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xeb, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xfe, 0xfd, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xc2, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xef, 0xfd, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf9, 0x80, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xc4, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf9, 0x19, 0xf9, 0xe5, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0x55, 0x00, 0x00, + 0x00, 0x00, 0xf9, 0x80, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xd3, 0x00, 0x00, + 0x00, 0x00, 0xf9, 0xd9, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xfe, 0xf8, 0x0a, + 0xf9, 0x40, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xd5, 0xff, 0x25, 0xff, 0xb5, 0xff, 0xfd, 0xff, 0xfd, 0x88, + 0xf9, 0x91, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x6d, 0xff, 0x25, 0xff, 0x49, 0xff, 0xfd, 0xff, 0xfd, 0xd4, + 0xf9, 0xca, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x25, 0xff, 0x25, 0xff, 0x25, 0xff, 0xd5, 0xff, 0xfd, 0xf2, + 0xf9, 0xdf, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x6d, 0xff, 0x25, 0xff, 0x49, 0xff, 0xfd, 0xff, 0xfd, 0xfc, + 0xf9, 0xf4, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x91, 0xff, 0x6d, 0xff, 0xb5, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xd5, 0xff, 0x25, 0xff, 0xb1, 0xff, 0xfd, 0xff, 0xfd, 0xff, + 0xf9, 0xf4, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xb1, 0xff, 0x25, 0xff, 0x25, 0xff, 0x25, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xfe, + 0xf9, 0xdf, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x8d, 0xff, 0x25, 0xff, 0x25, 0xff, 0x25, 0xff, 0xd5, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xfd, 0xf8, + 0xf9, 0xca, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0x8d, 0xff, 0x25, 0xff, 0x25, 0xff, 0x25, 0xff, 0xd5, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xf6, 0xff, 0xf9, 0xff, 0xfd, 0xed, + 0xf9, 0x91, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xd5, 0xff, 0x25, 0xff, 0x25, 0xff, 0x49, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf9, 0xff, 0xfd, 0xca, + 0xf9, 0x40, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xd5, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf5, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xf6, 0xff, 0xfd, 0xff, 0xfd, 0x88, + 0x00, 0x00, 0xf9, 0xd9, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf5, 0xff, 0xe5, 0xff, 0xe0, 0xff, 0xe9, 0xff, 0xf2, 0xff, 0xf6, 0xff, 0xfd, 0xfd, 0xfd, 0x1f, + 0x00, 0x00, 0xf9, 0x80, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xe9, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe5, 0xff, 0xed, 0xff, 0xfd, 0xcc, 0x00, 0x00, + 0x00, 0x00, 0xf9, 0x19, 0xf9, 0xe5, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xed, 0xff, 0xe5, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xf1, 0xff, 0xfd, 0x55, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf9, 0x80, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf9, 0xff, 0xed, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe9, 0xff, 0xf9, 0xd3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xc2, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf1, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xf9, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xeb, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xe5, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe5, 0xff, 0xf5, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xdf, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xf5, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe0, 0xff, 0xe9, 0xff, 0xf5, 0xfd, 0xfd, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0xaa, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xed, 0xff, 0xe9, 0xff, 0xe9, 0xff, 0xed, 0xff, 0xf5, 0xff, 0xfd, 0xf4, 0xfd, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x80, 0xf9, 0xe5, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xc3, 0xfd, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x19, 0xf9, 0x80, 0xf9, 0xdd, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xf9, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfd, 0xf1, 0xfd, 0xc5, 0xfd, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x4a, 0xf9, 0x98, 0xf9, 0xca, 0xf9, 0xdf, 0xf9, 0xf4, 0xf9, 0xf4, 0xf9, 0xdf, 0xf9, 0xca, 0xf9, 0x91, 0xf9, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif +#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0 + /*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe5, 0xfe, 0x1c, 0xa5, 0xfe, 0x88, 0xa5, 0xfe, 0xcb, 0xc5, 0xfe, 0xef, 0xc4, 0xfe, 0xf9, 0xe4, 0xfe, 0xfe, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xfc, 0xc4, 0xfe, 0xf5, 0xc4, 0xfe, 0xdb, 0xa4, 0xfe, 0x8a, 0x05, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xfe, 0x43, 0xa5, 0xfe, 0xc7, 0xe4, 0xfe, 0xfc, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xc4, 0xfe, 0xd9, 0xc4, 0xfe, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xfe, 0x9b, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xc4, 0xfe, 0xd7, 0xe4, 0xfe, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xfe, 0xb0, 0xc4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xfb, 0xe4, 0xfe, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xfe, 0xdf, 0xa5, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x05, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0xeb, 0x85, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xfe, 0x05, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0xc2, 0x25, 0xfe, 0xff, 0xc4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xc4, 0xfe, 0xef, 0xc5, 0xfe, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0x80, 0x25, 0xfe, 0xff, 0x65, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xa4, 0xfe, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x25, 0xfe, 0x19, 0x25, 0xfe, 0xe5, 0x25, 0xfe, 0xff, 0xa5, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xa5, 0xfe, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0xfe, 0x80, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xc4, 0xfe, 0xd3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x25, 0xfe, 0xd9, 0x25, 0xfe, 0xff, 0x65, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xfe, 0x63, 0xfe, 0x0a, + 0x05, 0xfe, 0x40, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x44, 0xc5, 0xff, 0x45, 0x29, 0xff, 0xa5, 0xac, 0xff, 0xe4, 0xfe, 0xff, 0xa5, 0xfe, 0x88, + 0x05, 0xfe, 0x91, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xa5, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe5, 0x6a, 0xff, 0x45, 0x29, 0xff, 0xa5, 0x39, 0xff, 0xe4, 0xfe, 0xff, 0xa5, 0xfe, 0xd4, + 0x25, 0xfe, 0xca, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0x84, 0xcd, 0xff, 0xc4, 0xfe, 0xf2, + 0x25, 0xfe, 0xdf, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x25, 0x73, 0xff, 0x45, 0x29, 0xff, 0xc5, 0x39, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xfc, + 0x25, 0xfe, 0xf4, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xc5, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe5, 0x8b, 0xff, 0x05, 0x6b, 0xff, 0xa5, 0xac, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x05, 0xbd, 0xff, 0x45, 0x29, 0xff, 0x85, 0xa4, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, + 0x25, 0xfe, 0xf4, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xa5, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x45, 0x9c, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0xe4, 0xdd, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xc4, 0xfe, 0xfe, + 0x25, 0xfe, 0xdf, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x25, 0x73, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0x05, 0xbd, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xc5, 0xfe, 0xff, 0xeb, 0xfd, 0xff, 0xc4, 0xfe, 0xf8, + 0x25, 0xfe, 0xca, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x65, 0x7b, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0x25, 0xbd, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x87, 0xfe, 0xff, 0x2f, 0xfd, 0xff, 0xeb, 0xfd, 0xff, 0xa5, 0xfe, 0xed, + 0x05, 0xfe, 0x91, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xc4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x05, 0xbd, 0xff, 0x45, 0x29, 0xff, 0x45, 0x29, 0xff, 0xe5, 0x41, 0xff, 0xa4, 0xf6, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x48, 0xfe, 0xff, 0x10, 0xfd, 0xff, 0xf1, 0xfc, 0xff, 0x49, 0xfe, 0xff, 0xa4, 0xfe, 0xca, + 0x05, 0xfe, 0x40, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xc4, 0xd5, 0xff, 0x25, 0xbd, 0xff, 0x84, 0xee, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x2a, 0xfd, 0xff, 0xf1, 0xfc, 0xff, 0xf1, 0xfc, 0xff, 0xf1, 0xfc, 0xff, 0x87, 0xfe, 0xff, 0xa5, 0xfe, 0x88, + 0x00, 0x00, 0x00, 0x25, 0xfe, 0xd9, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe5, 0xfc, 0xff, 0x65, 0xf1, 0xff, 0x83, 0xe8, 0xff, 0x08, 0xf2, 0xff, 0xee, 0xfb, 0xff, 0xf1, 0xfc, 0xff, 0xc5, 0xfe, 0xfd, 0x04, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0x05, 0xfe, 0x80, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xa5, 0xfd, 0xff, 0x66, 0xf2, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0xc4, 0xe8, 0xff, 0x2a, 0xfb, 0xff, 0xc5, 0xfe, 0xcc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x25, 0xfe, 0x19, 0x25, 0xfe, 0xe5, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0xc4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe5, 0xfd, 0xff, 0x27, 0xf3, 0xff, 0xc4, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0xa8, 0xfb, 0xff, 0xa5, 0xfe, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0x80, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xa6, 0xfd, 0xff, 0xc7, 0xf2, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0xe7, 0xf1, 0xff, 0x66, 0xfe, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0xc2, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x27, 0xfc, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x88, 0xfd, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0xeb, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xe5, 0xf0, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0xc4, 0xe8, 0xff, 0xc7, 0xfc, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xfe, 0xdf, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0xc4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0x06, 0xfd, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x83, 0xe8, 0xff, 0x47, 0xf2, 0xff, 0x27, 0xfd, 0xfd, 0xe5, 0xf6, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xfe, 0xaa, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe7, 0xf2, 0xff, 0xc6, 0xf1, 0xff, 0xe6, 0xf1, 0xff, 0xe7, 0xf2, 0xff, 0xa7, 0xfc, 0xff, 0xc5, 0xfe, 0xf4, 0xc4, 0xfe, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0x80, 0x25, 0xfe, 0xe5, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xc4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xe4, 0xfe, 0xff, 0xa4, 0xfe, 0xc3, 0xc5, 0xfe, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xfe, 0x19, 0x05, 0xfe, 0x80, 0x25, 0xfe, 0xdd, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x25, 0xfe, 0xff, 0x45, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0x85, 0xfe, 0xff, 0xa5, 0xfe, 0xff, 0xa5, 0xfe, 0xff, 0xa5, 0xfe, 0xff, 0xa5, 0xfe, 0xf1, 0xa4, 0xfe, 0xc5, 0xa5, 0xfe, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xfe, 0x4a, 0x25, 0xfe, 0x98, 0x25, 0xfe, 0xca, 0x25, 0xfe, 0xdf, 0x25, 0xfe, 0xf4, 0x25, 0xfe, 0xf4, 0x25, 0xfe, 0xdf, 0x25, 0xfe, 0xca, 0x05, 0xfe, 0x91, 0x05, 0xfe, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif +#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0 + /*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit BUT the 2 color bytes are swapped*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xe5, 0x1c, 0xfe, 0xa5, 0x88, 0xfe, 0xa5, 0xcb, 0xfe, 0xc5, 0xef, 0xfe, 0xc4, 0xf9, 0xfe, 0xe4, 0xfe, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xfc, 0xfe, 0xc4, 0xf5, 0xfe, 0xc4, 0xdb, 0xfe, 0xa4, 0x8a, 0xff, 0x05, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xa4, 0x43, 0xfe, 0xa5, 0xc7, 0xfe, 0xe4, 0xfc, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xc4, 0xd9, 0xfe, 0xc4, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x65, 0x9b, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xc4, 0xd7, 0xfe, 0xe4, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x25, 0xb0, 0xfe, 0xc4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xfb, 0xfe, 0xe4, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x25, 0xdf, 0xfe, 0xa5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xff, 0x05, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0xeb, 0xfe, 0x85, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xfe, 0xff, 0x05, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0xc2, 0xfe, 0x25, 0xff, 0xfe, 0xc4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xc4, 0xef, 0xfe, 0xc5, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0x80, 0xfe, 0x25, 0xff, 0xfe, 0x65, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xa4, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x25, 0x19, 0xfe, 0x25, 0xe5, 0xfe, 0x25, 0xff, 0xfe, 0xa5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xa5, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x05, 0x80, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xc4, 0xd3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x25, 0xd9, 0xfe, 0x25, 0xff, 0xfe, 0x65, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xfe, 0xfe, 0x63, 0x0a, + 0xfe, 0x05, 0x40, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xc5, 0x44, 0xff, 0x29, 0x45, 0xff, 0xac, 0xa5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xa5, 0x88, + 0xfe, 0x05, 0x91, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xa5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0x6a, 0xe5, 0xff, 0x29, 0x45, 0xff, 0x39, 0xa5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xa5, 0xd4, + 0xfe, 0x25, 0xca, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0xcd, 0x84, 0xff, 0xfe, 0xc4, 0xf2, + 0xfe, 0x25, 0xdf, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0x73, 0x25, 0xff, 0x29, 0x45, 0xff, 0x39, 0xc5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xfc, + 0xfe, 0x25, 0xf4, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xc5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0x8b, 0xe5, 0xff, 0x6b, 0x05, 0xff, 0xac, 0xa5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xbd, 0x05, 0xff, 0x29, 0x45, 0xff, 0xa4, 0x85, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, + 0xfe, 0x25, 0xf4, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xa5, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0x9c, 0x45, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0xdd, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xc4, 0xfe, + 0xfe, 0x25, 0xdf, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0x73, 0x25, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0xbd, 0x05, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xc5, 0xff, 0xfd, 0xeb, 0xff, 0xfe, 0xc4, 0xf8, + 0xfe, 0x25, 0xca, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0x7b, 0x65, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0xbd, 0x25, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0x87, 0xff, 0xfd, 0x2f, 0xff, 0xfd, 0xeb, 0xff, 0xfe, 0xa5, 0xed, + 0xfe, 0x05, 0x91, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xc4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xbd, 0x05, 0xff, 0x29, 0x45, 0xff, 0x29, 0x45, 0xff, 0x41, 0xe5, 0xff, 0xf6, 0xa4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0x48, 0xff, 0xfd, 0x10, 0xff, 0xfc, 0xf1, 0xff, 0xfe, 0x49, 0xff, 0xfe, 0xa4, 0xca, + 0xfe, 0x05, 0x40, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xd5, 0xc4, 0xff, 0xbd, 0x25, 0xff, 0xee, 0x84, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfd, 0x2a, 0xff, 0xfc, 0xf1, 0xff, 0xfc, 0xf1, 0xff, 0xfc, 0xf1, 0xff, 0xfe, 0x87, 0xff, 0xfe, 0xa5, 0x88, + 0x00, 0x00, 0x00, 0xfe, 0x25, 0xd9, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfc, 0xe5, 0xff, 0xf1, 0x65, 0xff, 0xe8, 0x83, 0xff, 0xf2, 0x08, 0xff, 0xfb, 0xee, 0xff, 0xfc, 0xf1, 0xff, 0xfe, 0xc5, 0xfd, 0xff, 0x04, 0x1f, + 0x00, 0x00, 0x00, 0xfe, 0x05, 0x80, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfd, 0xa5, 0xff, 0xf2, 0x66, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0xc4, 0xff, 0xfb, 0x2a, 0xff, 0xfe, 0xc5, 0xcc, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0x25, 0x19, 0xfe, 0x25, 0xe5, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0xc4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfd, 0xe5, 0xff, 0xf3, 0x27, 0xff, 0xe8, 0xc4, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xfb, 0xa8, 0xff, 0xfe, 0xa5, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0x80, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfd, 0xa6, 0xff, 0xf2, 0xc7, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xf1, 0xe7, 0xff, 0xfe, 0x66, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0xc2, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfc, 0x27, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xfd, 0x88, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0xeb, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0x85, 0xff, 0xf0, 0xe5, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0xc4, 0xff, 0xfc, 0xc7, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x25, 0xdf, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0xc4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfd, 0x06, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xe8, 0x83, 0xff, 0xf2, 0x47, 0xff, 0xfd, 0x27, 0xfd, 0xf6, 0xe5, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x25, 0xaa, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xf2, 0xe7, 0xff, 0xf1, 0xc6, 0xff, 0xf1, 0xe6, 0xff, 0xf2, 0xe7, 0xff, 0xfc, 0xa7, 0xff, 0xfe, 0xc5, 0xf4, 0xfe, 0xc4, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0x80, 0xfe, 0x25, 0xe5, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0xc4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xe4, 0xff, 0xfe, 0xa4, 0xc3, 0xfe, 0xc5, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x25, 0x19, 0xfe, 0x05, 0x80, 0xfe, 0x25, 0xdd, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x25, 0xff, 0xfe, 0x45, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0x85, 0xff, 0xfe, 0xa5, 0xff, 0xfe, 0xa5, 0xff, 0xfe, 0xa5, 0xff, 0xfe, 0xa5, 0xf1, 0xfe, 0xa4, 0xc5, 0xfe, 0xa5, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x05, 0x4a, 0xfe, 0x25, 0x98, 0xfe, 0x25, 0xca, 0xfe, 0x25, 0xdf, 0xfe, 0x25, 0xf4, 0xfe, 0x25, 0xf4, 0xfe, 0x25, 0xdf, 0xfe, 0x25, 0xca, 0xfe, 0x05, 0x91, 0xfe, 0x05, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif +#if LV_COLOR_DEPTH == 32 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xdb, 0xff, 0x1c, 0x25, 0xd4, 0xfb, 0x88, 0x24, 0xd3, 0xfb, 0xcb, 0x24, 0xd6, 0xfb, 0xef, 0x23, 0xd8, 0xfb, 0xf9, 0x22, 0xda, 0xfc, 0xfe, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xda, 0xfc, 0xfc, 0x23, 0xd8, 0xfc, 0xf5, 0x23, 0xd6, 0xfc, 0xdb, 0x23, 0xd5, 0xfb, 0x8a, 0x24, 0xe1, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xd5, 0xff, 0x43, 0x24, 0xd3, 0xfa, 0xc7, 0x22, 0xdc, 0xfc, 0xfc, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xd7, 0xfa, 0xd9, 0x23, 0xd6, 0xff, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0xca, 0xf8, 0x9b, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xd7, 0xfb, 0xd7, 0x23, 0xdc, 0xff, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xc5, 0xf9, 0xb0, 0x23, 0xd7, 0xfb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xda, 0xfc, 0xfb, 0x22, 0xdd, 0xff, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc2, 0xf9, 0xdf, 0x24, 0xd2, 0xfb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x24, 0xe1, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc1, 0xf8, 0xeb, 0x25, 0xcf, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdb, 0xfc, 0xfe, 0x24, 0xe1, 0xff, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xc1, 0xf8, 0xc2, 0x27, 0xc2, 0xf9, 0xff, 0x23, 0xd8, 0xfb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xd8, 0xfb, 0xef, 0x27, 0xd8, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc1, 0xf7, 0x80, 0x28, 0xc2, 0xf9, 0xff, 0x26, 0xcc, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xd4, 0xfb, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x29, 0xc2, 0xff, 0x19, 0x28, 0xc3, 0xf9, 0xe5, 0x28, 0xc2, 0xf9, 0xff, 0x24, 0xd5, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x24, 0xd5, 0xfc, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0xc1, 0xf7, 0x80, 0x28, 0xc2, 0xf9, 0xff, 0x26, 0xc9, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xd6, 0xfb, 0xd3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0xc2, 0xf9, 0xd9, 0x28, 0xc2, 0xf9, 0xff, 0x26, 0xcd, 0xfb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfb, 0xfe, 0x1a, 0xcc, 0xff, 0x0a, + 0x28, 0xbf, 0xf7, 0x40, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x24, 0xd1, 0xfb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xa8, 0xbf, 0xff, 0x28, 0x28, 0x28, 0xff, 0x24, 0x94, 0xa7, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x25, 0xd4, 0xfb, 0x88, + 0x28, 0xc1, 0xf8, 0x91, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x24, 0xd5, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x27, 0x5c, 0x65, 0xff, 0x28, 0x28, 0x28, 0xff, 0x27, 0x35, 0x37, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x24, 0xd4, 0xfb, 0xd4, + 0x28, 0xc2, 0xf9, 0xca, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x22, 0xda, 0xfd, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x23, 0xaf, 0xc7, 0xff, 0x23, 0xd7, 0xfb, 0xf2, + 0x28, 0xc2, 0xf9, 0xdf, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x23, 0xda, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x26, 0x64, 0x6f, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x37, 0x3a, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdb, 0xfc, 0xfc, + 0x28, 0xc2, 0xfa, 0xf4, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x24, 0xd7, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x26, 0x7b, 0x8a, 0xff, 0x26, 0x60, 0x6a, 0xff, 0x24, 0x94, 0xa7, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x24, 0xa0, 0xb5, 0xff, 0x28, 0x28, 0x28, 0xff, 0x24, 0x8f, 0xa1, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xdb, 0xfb, 0xff, + 0x28, 0xc2, 0xfa, 0xf4, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x24, 0xd2, 0xfb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x25, 0x87, 0x98, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x23, 0xbc, 0xd7, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xd8, 0xfb, 0xfe, + 0x28, 0xc2, 0xf9, 0xdf, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x25, 0xcf, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x26, 0x65, 0x70, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x25, 0xa0, 0xb5, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x27, 0xd9, 0xfb, 0xff, 0x55, 0xbb, 0xfc, 0xff, 0x23, 0xd7, 0xfb, 0xf8, + 0x28, 0xc2, 0xf9, 0xca, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x27, 0xc7, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x26, 0x6d, 0x79, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x24, 0xa5, 0xbb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x37, 0xce, 0xfc, 0xff, 0x7b, 0xa3, 0xfc, 0xff, 0x55, 0xbb, 0xfc, 0xff, 0x24, 0xd5, 0xfb, 0xed, + 0x28, 0xc1, 0xf8, 0x91, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x23, 0xd6, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x24, 0xa1, 0xb7, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x28, 0x28, 0xff, 0x28, 0x3c, 0x40, 0xff, 0x22, 0xd3, 0xf1, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x43, 0xc7, 0xfc, 0xff, 0x82, 0x9f, 0xfc, 0xff, 0x88, 0x9b, 0xfc, 0xff, 0x45, 0xc6, 0xfc, 0xff, 0x23, 0xd2, 0xfa, 0xca, + 0x28, 0xbf, 0xf7, 0x40, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x25, 0xcf, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xb8, 0xd2, 0xff, 0x24, 0xa3, 0xb9, 0xff, 0x22, 0xce, 0xeb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x4d, 0xa4, 0xf9, 0xff, 0x88, 0x9b, 0xfc, 0xff, 0x88, 0x9b, 0xfc, 0xff, 0x88, 0x9b, 0xfc, 0xff, 0x35, 0xd0, 0xfc, 0xff, 0x25, 0xd2, 0xf8, 0x88, + 0x00, 0x00, 0x00, 0x00, 0x28, 0xc2, 0xf9, 0xd9, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x27, 0xc5, 0xfa, 0xff, 0x23, 0xda, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x2a, 0x9c, 0xf6, 0xff, 0x28, 0x2a, 0xed, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x3f, 0x3f, 0xf0, 0xff, 0x70, 0x7b, 0xf8, 0xff, 0x88, 0x9b, 0xfc, 0xff, 0x26, 0xd9, 0xfc, 0xfd, 0x21, 0xde, 0xf7, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x28, 0xc1, 0xf7, 0x80, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x25, 0xcf, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x2a, 0xb4, 0xf9, 0xff, 0x2f, 0x4d, 0xf0, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x22, 0x19, 0xeb, 0xff, 0x4d, 0x64, 0xf4, 0xff, 0x24, 0xd6, 0xfb, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x29, 0xc2, 0xff, 0x19, 0x28, 0xc3, 0xf9, 0xe5, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x27, 0xc5, 0xfa, 0xff, 0x23, 0xd8, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x2a, 0xbb, 0xf9, 0xff, 0x37, 0x64, 0xf3, 0xff, 0x21, 0x19, 0xeb, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x41, 0x74, 0xf6, 0xff, 0x24, 0xd5, 0xfc, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc1, 0xf7, 0x80, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x26, 0xc8, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x2e, 0xb2, 0xfa, 0xff, 0x35, 0x57, 0xf2, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x36, 0x3c, 0xf0, 0xff, 0x30, 0xcd, 0xfb, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0xc1, 0xf8, 0xc2, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x26, 0xc8, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x36, 0x82, 0xf5, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x41, 0xb1, 0xfa, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc1, 0xf8, 0xeb, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x27, 0xc7, 0xfa, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x25, 0xd0, 0xfa, 0xff, 0x24, 0x1d, 0xed, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x21, 0x18, 0xeb, 0xff, 0x39, 0x99, 0xf8, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc2, 0xf9, 0xdf, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x27, 0xc7, 0xfa, 0xff, 0x23, 0xd8, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x32, 0xa1, 0xf8, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x1a, 0x0f, 0xea, 0xff, 0x37, 0x49, 0xf1, 0xff, 0x3b, 0xa4, 0xfa, 0xfd, 0x24, 0xdb, 0xed, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xc2, 0xf9, 0xaa, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc5, 0xfa, 0xff, 0x25, 0xd0, 0xfa, 0xff, 0x22, 0xdb, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x36, 0x5c, 0xf3, 0xff, 0x2f, 0x36, 0xee, 0xff, 0x31, 0x3d, 0xef, 0xff, 0x37, 0x5a, 0xf3, 0xff, 0x36, 0x94, 0xf8, 0xff, 0x24, 0xd7, 0xfb, 0xf4, 0x20, 0xd7, 0xf7, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xc1, 0xf7, 0x80, 0x28, 0xc3, 0xf9, 0xe5, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x27, 0xc6, 0xf9, 0xff, 0x24, 0xd1, 0xfa, 0xff, 0x23, 0xd8, 0xfb, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x22, 0xdc, 0xfc, 0xff, 0x23, 0xd5, 0xfc, 0xc3, 0x27, 0xd8, 0xff, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xc2, 0xff, 0x19, 0x28, 0xc1, 0xf7, 0x80, 0x28, 0xc2, 0xf8, 0xdd, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x28, 0xc2, 0xf9, 0xff, 0x26, 0xc7, 0xf9, 0xff, 0x25, 0xcf, 0xfa, 0xff, 0x25, 0xd1, 0xfa, 0xff, 0x24, 0xd3, 0xfb, 0xff, 0x24, 0xd5, 0xfb, 0xff, 0x24, 0xd4, 0xfa, 0xff, 0x24, 0xd3, 0xfb, 0xf1, 0x23, 0xd4, 0xfa, 0xc5, 0x24, 0xd5, 0xfc, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xc1, 0xf8, 0x4a, 0x28, 0xc3, 0xf8, 0x98, 0x28, 0xc2, 0xf9, 0xca, 0x28, 0xc2, 0xf9, 0xdf, 0x28, 0xc2, 0xfa, 0xf4, 0x28, 0xc2, 0xfa, 0xf4, 0x28, 0xc2, 0xf9, 0xdf, 0x28, 0xc2, 0xf9, 0xca, 0x28, 0xc1, 0xf8, 0x91, 0x28, 0xbf, 0xf7, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +#endif +}; + +const lv_img_dsc_t image_emoji = { + .header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA, + .header.always_zero = 0, + .header.reserved = 0, + .header.w = 32, + .header.h = 32, + .data_size = 1024 * LV_IMG_PX_SIZE_ALPHA_BYTE, + .data = image_emoji_map, +}; diff --git a/examples/USB_Host_Keyboard_Mouse/src/hid.h b/examples/USB_Host_Keyboard_Mouse/src/hid.h new file mode 100644 index 0000000..c10604c --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/src/hid.h @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief HID Subclass + * + * @see 4.2 Subclass, p.8 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef enum { + HID_SUBCLASS_NO_SUBCLASS = 0x00, + HID_SUBCLASS_BOOT_INTERFACE = 0x01 +} __attribute__((packed)) hid_subclass_t; + +/** + * @brief HID Protocols + * + * @see 4.3 Protocols, p.9 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef enum { + HID_PROTOCOL_NONE = 0x00, + HID_PROTOCOL_KEYBOARD = 0x01, + HID_PROTOCOL_MOUSE = 0x02, + HID_PROTOCOL_MAX +} __attribute__((packed)) hid_protocol_t; + +/** + * @brief HID Descriptor + * + * @see 6.2.1 HID Descriptor, p.22 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef struct { + uint8_t bLength; // Numeric expression that is the total size of the HID descriptor + uint8_t bDescriptorType; // Constant name specifying type of HID descriptor + uint16_t bcdHID; // Numeric expression identifying the HIDClass Specification release + uint8_t bCountryCode; // Numeric expression identifying country code of the localized hardware + uint8_t bNumDescriptors; // Numeric expression specifying the number of class descriptors (always at least one i.e. Report descriptor.) + uint8_t bReportDescriptorType; // Constant name identifying type of class descriptor. See Section 7.1.2: Set_Descriptor Request for a table of class descriptor constants + uint16_t wReportDescriptorLength; // Numeric expression that is the total size of the Report descriptor + // Optional descriptors may follow further +} __attribute__((packed)) hid_descriptor_t; + +/** + * @brief HID Country Codes + * + * @see 6.2.1 HID Descriptor, p.23 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ + +typedef enum { + HID_COUNTRY_CODE_NOT_SUPPORTED = 0x00, + HID_COUNTRY_CODE_ARABIC = 0x01, + HID_COUNTRY_CODE_BELGIAN = 0x02, + HID_COUNTRY_CODE_CANADIAN_BILINGUAL = 0x03, + HID_COUNTRY_CODE_CANADIAN_FRENCH = 0x04, + HID_COUNTRY_CODE_CZECH = 0x05, + HID_COUNTRY_CODE_DANISH = 0x06, + HID_COUNTRY_CODE_FINNISH = 0x07, + HID_COUNTRY_CODE_FRENCH = 0x08, + HID_COUNTRY_CODE_GERMAN = 0x09, + HID_COUNTRY_CODE_GREEK = 0x0A, + HID_COUNTRY_CODE_HEBREW = 0x0B, + HID_COUNTRY_CODE_HUNGARY = 0x0C, + HID_COUNTRY_CODE_ISO = 0x0D, + HID_COUNTRY_CODE_ITALIAN = 0x0E, + HID_COUNTRY_CODE_JAPAN = 0x0F, + HID_COUNTRY_CODE_KOREAN = 0x10, + HID_COUNTRY_CODE_LATIN_AMERICAN = 0x11, + HID_COUNTRY_CODE_NETHERLANDS = 0x12, + HID_COUNTRY_CODE_NORWEGIAN = 0x13, + HID_COUNTRY_CODE_PERSIAN = 0x14, + HID_COUNTRY_CODE_POLAND = 0x15, + HID_COUNTRY_CODE_PORTUGUESE = 0x16, + HID_COUNTRY_CODE_RUSSIA = 0x17, + HID_COUNTRY_CODE_SLOVAKIA = 0x18, + HID_COUNTRY_CODE_SPANISH = 0x19, + HID_COUNTRY_CODE_SWEDISH = 0x1A, + HID_COUNTRY_CODE_SWISS_F = 0x1B, + HID_COUNTRY_CODE_SWISS_G = 0x1C, + HID_COUNTRY_CODE_SWITZERLAND = 0x1D, + HID_COUNTRY_CODE_TAIWAN = 0x1E, + HID_COUNTRY_CODE_TURKISH_Q = 0x1F, + HID_COUNTRY_CODE_UK = 0x20, + HID_COUNTRY_CODE_US = 0x21, + HID_COUNTRY_CODE_YUGOSLAVIA = 0x22, + HID_COUNTRY_CODE_TURKISH_F = 0x23 +} __attribute__((packed)) hid_country_code_t; + +/** + * @brief HID Class Descriptor Types + * + * @see 7.1, p.49 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef enum { + HID_CLASS_DESCRIPTOR_TYPE_HID = 0x21, + HID_CLASS_DESCRIPTOR_TYPE_REPORT = 0x22, + HID_CLASS_DESCRIPTOR_TYPE_PHYSICAL = 0x23 +} __attribute__((packed)) hid_class_descritpor_type_t; + +/** + * @brief HID Class-Specific Requests + * + * @see 7.2, p.50 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef enum { + HID_CLASS_SPECIFIC_REQ_GET_REPORT = 0x01, + HID_CLASS_SPECIFIC_REQ_GET_IDLE = 0x02, + HID_CLASS_SPECIFIC_REQ_GET_PROTOCOL = 0x03, + HID_CLASS_SPECIFIC_REQ_SET_REPORT = 0x09, + HID_CLASS_SPECIFIC_REQ_SET_IDLE = 0x0A, + HID_CLASS_SPECIFIC_REQ_SET_PROTOCOL = 0x0B +} __attribute__((packed)) hid_class_specific_req_t; + +/** + * @brief HID Report Types + * + * @see 7.2.1, p.51 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef enum { + HID_REPORT_TYPE_INPUT = 0x01, + HID_REPORT_TYPE_OUTPUT = 0x02, + HID_REPORT_TYPE_FEATURE = 0x03, +} __attribute__((packed)) hid_report_type_t; + +/** + * @brief HID Report protocol + * + * @see 7.2.5/7.2.6, p.54 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef enum { + HID_REPORT_PROTOCOL_BOOT = 0x00, + HID_REPORT_PROTOCOL_REPORT = 0x01, + HID_REPORT_PROTOCOL_MAX +} __attribute__((packed)) hid_report_protocol_t; + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/examples/USB_Host_Keyboard_Mouse/src/hid_host.c b/examples/USB_Host_Keyboard_Mouse/src/hid_host.c new file mode 100644 index 0000000..cfb82a1 --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/src/hid_host.c @@ -0,0 +1,1503 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include "esp_log.h" +#include "esp_check.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "usb/usb_host.h" +#include "hid_host.h" + +// HID spinlock +static portMUX_TYPE hid_lock = portMUX_INITIALIZER_UNLOCKED; +#define HID_ENTER_CRITICAL() portENTER_CRITICAL(&hid_lock) +#define HID_EXIT_CRITICAL() portEXIT_CRITICAL(&hid_lock) + +// HID verification macroses +#define HID_GOTO_ON_FALSE_CRITICAL(exp, err) \ + do { \ + if(!(exp)) { \ + HID_EXIT_CRITICAL(); \ + ret = err; \ + goto fail; \ + } \ + } while(0) + +#define HID_RETURN_ON_FALSE_CRITICAL(exp, err) \ + do { \ + if(!(exp)) { \ + HID_EXIT_CRITICAL(); \ + return err; \ + } \ + } while(0) + +#define HID_GOTO_ON_ERROR(exp, msg) ESP_GOTO_ON_ERROR(exp, fail, TAG, msg) + +#define HID_GOTO_ON_FALSE(exp, err, msg) ESP_GOTO_ON_FALSE( (exp), err, fail, TAG, msg ) + +#define HID_RETURN_ON_ERROR(exp, msg) ESP_RETURN_ON_ERROR((exp), TAG, msg) + +#define HID_RETURN_ON_FALSE(exp, err, msg) ESP_RETURN_ON_FALSE( (exp), (err), TAG, msg) + +#define HID_RETURN_ON_INVALID_ARG(exp) ESP_RETURN_ON_FALSE((exp) != NULL, ESP_ERR_INVALID_ARG, TAG, "Argument error") + +static const char *TAG = "hid-host"; + +#define DEFAULT_TIMEOUT_MS (5000) + +/** + * @brief HID Device structure. + * + */ +typedef struct hid_host_device { + STAILQ_ENTRY(hid_host_device) tailq_entry; /**< HID device queue */ + SemaphoreHandle_t device_busy; /**< HID device main mutex */ + SemaphoreHandle_t ctrl_xfer_done; /**< Control transfer semaphore */ + usb_transfer_t *ctrl_xfer; /**< Pointer to control transfer buffer */ + usb_device_handle_t dev_hdl; /**< USB device handle */ + uint8_t dev_addr; /**< USB devce address */ +} hid_device_t; + +/** + * @brief HID Interface state +*/ +typedef enum { + HID_INTERFACE_STATE_NOT_INITIALIZED = 0x00, /**< HID Interface not initialized */ + HID_INTERFACE_STATE_IDLE, /**< HID Interface has been found in connected USB device */ + HID_INTERFACE_STATE_READY, /**< HID Interface opened and ready to start transfer */ + HID_INTERFACE_STATE_ACTIVE, /**< HID Interface is in use */ + HID_INTERFACE_STATE_WAIT_USER_DELETION, /**< HID Interface wait user to be removed */ + HID_INTERFACE_STATE_MAX +} hid_iface_state_t; + +/** + * @brief HID Interface structure in device to interact with. After HID device opening keeps the interface configuration + * + */ +typedef struct hid_interface { + STAILQ_ENTRY(hid_interface) tailq_entry; + hid_device_t *parent; /**< Parent USB HID device */ + hid_host_dev_params_t dev_params; /**< USB device parameters */ + uint8_t ep_in; /**< Interrupt IN EP number */ + uint16_t ep_in_mps; /**< Interrupt IN max size */ + uint8_t country_code; /**< Country code */ + uint16_t report_desc_size; /**< Size of Report */ + uint8_t *report_desc; /**< Pointer to HID Report */ + usb_transfer_t *in_xfer; /**< Pointer to IN transfer buffer */ + hid_host_interface_event_cb_t user_cb; /**< Interface application callback */ + void *user_cb_arg; /**< Interface application callback arg */ + hid_iface_state_t state; /**< Interface state */ +} hid_iface_t; + +/** + * @brief HID driver default context + * + * This context is created during HID Host install. + */ +typedef struct { + STAILQ_HEAD(devices, hid_host_device) hid_devices_tailq; /**< STAILQ of HID interfaces */ + STAILQ_HEAD(interfaces, hid_interface) hid_ifaces_tailq; /**< STAILQ of HID interfaces */ + usb_host_client_handle_t client_handle; /**< Client task handle */ + hid_host_driver_event_cb_t user_cb; /**< User application callback */ + void *user_arg; /**< User application callback args */ + SemaphoreHandle_t all_events_handled; /**< Events handler semaphore */ + volatile bool end_client_event_handling; /**< Client event handling flag */ +} hid_driver_t; + +static hid_driver_t *s_hid_driver; /**< Internal pointer to HID driver */ + + +// ----------------------- Private Prototypes ---------------------------------- + +static esp_err_t hid_host_install_device(uint8_t dev_addr, + usb_device_handle_t dev_hdl, + hid_device_t **hid_device); + + +static esp_err_t hid_host_uninstall_device(hid_device_t *hid_device); + +// --------------------------- Internal Logic ---------------------------------- +/** + * @brief HID class specific request +*/ +typedef struct hid_class_request { + uint8_t bRequest; /**< bRequest */ + uint16_t wValue; /**< wValue: Report Type and Report ID */ + uint16_t wIndex; /**< wIndex: Interface */ + uint16_t wLength; /**< wLength: Report Length */ + uint8_t *data; /**< Pointer to data */ +} hid_class_request_t; + +/** + * @brief USB Event handler + * + * Handle all USB related events such as USB host (usbh) events or hub events from USB hardware + * + * @param[in] arg Argument, does not used + */ +static void event_handler_task(void *arg) +{ + while (1) { + /* Here wee need a timeout 50 ms to handle end_client_event_handling flag + * during situation when all devices were removed and it is time to remove + * and destroy everything. + */ + usb_host_client_handle_events(s_hid_driver->client_handle, portMAX_DELAY); + + if (s_hid_driver->end_client_event_handling) { + break; + } + } + ESP_ERROR_CHECK( usb_host_client_deregister(s_hid_driver->client_handle) ); + xSemaphoreGive(s_hid_driver->all_events_handled); + vTaskDelete(NULL); +} + +/** + * @brief Return HID device in devices list by USB device handle + * + * @param[in] usb_device_handle_t USB device handle + * @return hid_device_t Pointer to device, NULL if device not present + */ +static hid_device_t *get_hid_device_by_handle(usb_device_handle_t usb_handle) +{ + hid_device_t *device = NULL; + + HID_ENTER_CRITICAL(); + STAILQ_FOREACH(device, &s_hid_driver->hid_devices_tailq, tailq_entry) { + if (usb_handle == device->dev_hdl) { + HID_EXIT_CRITICAL(); + return device; + } + } + HID_EXIT_CRITICAL(); + return NULL; +} + +/** + * @brief Return HID Device fron the transfer context + * + * @param[in] xfer USB transfer struct + * @return hid_device_t Pointer to HID Device + */ +static inline hid_device_t *get_hid_device_from_context(usb_transfer_t *xfer) +{ + return (hid_device_t *)xfer->context; +} + +/** + * @brief Get HID Interface pointer by Endpoint address + * + * @param[in] ep_addr Endpoint address + * @return hid_iface_t Pointer to HID Interface configuration structure + */ +static hid_iface_t *get_interface_by_ep(uint8_t ep_addr) +{ + hid_iface_t *interface = NULL; + + HID_ENTER_CRITICAL(); + STAILQ_FOREACH(interface, &s_hid_driver->hid_ifaces_tailq, tailq_entry) { + if (ep_addr == interface->ep_in) { + HID_EXIT_CRITICAL(); + return interface; + } + } + + HID_EXIT_CRITICAL(); + return NULL; +} + +/** + * @brief Verify presence of Interface in the RAM list + * + * @param[in] iface Pointer to an Interface structure + * @return true Interface is in the list + * @return false Interface is not in the list + */ +static inline bool is_interface_in_list(hid_iface_t *iface) +{ + hid_iface_t *interface = NULL; + + HID_ENTER_CRITICAL(); + STAILQ_FOREACH(interface, &s_hid_driver->hid_ifaces_tailq, tailq_entry) { + if (iface == interface) { + HID_EXIT_CRITICAL(); + return true; + } + } + + HID_EXIT_CRITICAL(); + return false; +} + +/** + * @brief Get HID Interface pointer by external HID Device handle with verification in RAM list + * + * @param[in] hid_dev_handle HID Device handle + * @return hid_iface_t Pointer to an Interface structure + */ +static hid_iface_t *get_iface_by_handle(hid_host_device_handle_t hid_dev_handle) +{ + hid_iface_t *hid_iface = (hid_iface_t *) hid_dev_handle; + + if (!is_interface_in_list(hid_iface)) { + ESP_LOGE(TAG, "HID device handle not found"); + return NULL; + } + + return hid_iface; +} + +/** + * @brief Check HID interface descriptor present + * + * @param[in] config_desc Pointer to Configuration Descriptor + * @return esp_err_t + */ +static bool hid_interface_present(const usb_config_desc_t *config_desc) +{ + const usb_intf_desc_t *iface_desc = NULL; + int offset = 0; + + for (int num = 0; num < config_desc->bNumInterfaces; num++) { + iface_desc = usb_parse_interface_descriptor(config_desc, num, 0, &offset); + if (USB_CLASS_HID == iface_desc->bInterfaceClass) { + return true; + } + } + return false; +} + +/** + * @brief HID Interface user callback function. + * + * @param[in] hid_iface Pointer to an Interface structure + * @param[in] event_id HID Interface event + */ +static inline void hid_host_user_interface_callback(hid_iface_t *hid_iface, + const hid_host_interface_event_t event) +{ + assert(hid_iface); + + hid_host_dev_params_t *dev_params = &hid_iface->dev_params; + + assert(dev_params); + + if (hid_iface->user_cb) { + hid_iface->user_cb(hid_iface, event, hid_iface->user_cb_arg); + } +} + +/** + * @brief HID Device user callback function. + * + * @param[in] event_id HID Device event + * @param[in] dev_params HID Device parameters + */ +static inline void hid_host_user_device_callback(hid_iface_t *hid_iface, + const hid_host_driver_event_t event) +{ + assert(hid_iface); + + hid_host_dev_params_t *dev_params = &hid_iface->dev_params; + + assert(dev_params); + + if (s_hid_driver && s_hid_driver->user_cb) { + s_hid_driver->user_cb(hid_iface, event, s_hid_driver->user_arg); + } +} + +/** + * @brief Add interface in a list + * + * @param[in] hid_device HID device handle + * @param[in] iface_desc Pointer to an Interface descriptor + * @param[in] hid_desc Pointer to an HID device descriptor + * @param[in] ep_desc Pointer to an EP descriptor + * @return esp_err_t + */ +static esp_err_t hid_host_add_interface(hid_device_t *hid_device, + const usb_intf_desc_t *iface_desc, + const hid_descriptor_t *hid_desc, + const usb_ep_desc_t *ep_in_desc) +{ + hid_iface_t *hid_iface = calloc(1, sizeof(hid_iface_t)); + + HID_RETURN_ON_FALSE(hid_iface, + ESP_ERR_NO_MEM, + "Unable to allocate memory"); + + HID_ENTER_CRITICAL(); + hid_iface->parent = hid_device; + hid_iface->state = HID_INTERFACE_STATE_NOT_INITIALIZED; + hid_iface->dev_params.addr = hid_device->dev_addr; + + if (iface_desc) { + hid_iface->dev_params.iface_num = iface_desc->bInterfaceNumber; + hid_iface->dev_params.sub_class = iface_desc->bInterfaceSubClass; + hid_iface->dev_params.proto = iface_desc->bInterfaceProtocol; + } + + if (hid_desc) { + hid_iface->country_code = hid_desc->bCountryCode; + hid_iface->report_desc_size = hid_desc->wReportDescriptorLength; + } + + // EP IN && INT Type + if (ep_in_desc) { + if ( (ep_in_desc->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) && + (ep_in_desc->bmAttributes & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) ) { + hid_iface->ep_in = ep_in_desc->bEndpointAddress; + hid_iface->ep_in_mps = USB_EP_DESC_GET_MPS(ep_in_desc); + } else { + ESP_EARLY_LOGE(TAG, "HID device EP IN %#X configuration error", + ep_in_desc->bEndpointAddress); + } + } + + if (iface_desc && hid_desc && ep_in_desc) { + hid_iface->state = HID_INTERFACE_STATE_IDLE; + } + + STAILQ_INSERT_TAIL(&s_hid_driver->hid_ifaces_tailq, hid_iface, tailq_entry); + HID_EXIT_CRITICAL(); + + return ESP_OK; +} + +/** + * @brief Notify user about the connected Interfaces + * + * @param[in] hid_device Pointer to HID device structure + */ +static void hid_host_notify_interface_connected(hid_device_t *hid_device) +{ + HID_ENTER_CRITICAL(); + hid_iface_t *iface = STAILQ_FIRST(&s_hid_driver->hid_ifaces_tailq); + hid_iface_t *tmp = NULL; + + while (iface != NULL) { + tmp = STAILQ_NEXT(iface, tailq_entry); + HID_EXIT_CRITICAL(); + + if (iface->parent && (iface->parent->dev_addr == hid_device->dev_addr)) { + hid_host_user_device_callback(iface, HID_HOST_DRIVER_EVENT_CONNECTED); + } + iface = tmp; + + HID_ENTER_CRITICAL(); + } + HID_EXIT_CRITICAL(); +} + +/** + * @brief Create a list of available interfaces in RAM + * + * @param[in] hid_device Pointer to HID device structure + * @param[in] dev_addr USB device physical address + * @param[in] sub_class USB HID SubClass value + * @return esp_err_t + */ +static esp_err_t hid_host_interface_list_create(hid_device_t *hid_device, + const usb_config_desc_t *config_desc) +{ + size_t total_length = config_desc->wTotalLength; + const usb_intf_desc_t *iface_desc = NULL; + const hid_descriptor_t *hid_desc = NULL; + const usb_ep_desc_t *ep_desc = NULL; + const usb_ep_desc_t *ep_in_desc = NULL; + int offset = 0; + + // For every Interface + for (int i = 0; i < config_desc->bNumInterfaces; i++) { + iface_desc = usb_parse_interface_descriptor(config_desc, i, 0, &offset); + hid_desc = NULL; + ep_in_desc = NULL; + + if (USB_CLASS_HID == iface_desc->bInterfaceClass) { + // HID descriptor + hid_desc = (const hid_descriptor_t *) usb_parse_next_descriptor_of_type((const usb_standard_desc_t *) iface_desc, + total_length, + HID_CLASS_DESCRIPTOR_TYPE_HID, + &offset); + if (!hid_desc) { + return ESP_ERR_NOT_FOUND; + } + + // EP descriptors for Interface + for (int i = 0; i < iface_desc->bNumEndpoints; i++) { + int ep_offset = 0; + ep_desc = usb_parse_endpoint_descriptor_by_index(iface_desc, i, total_length, &ep_offset); + + if (ep_desc) { + if (USB_EP_DESC_GET_EP_DIR(ep_desc)) { + ep_in_desc = ep_desc; + } + } else { + return ESP_ERR_NOT_FOUND; + } + } // for every EP within Interface + + // Add Interface to the list + HID_RETURN_ON_ERROR( hid_host_add_interface(hid_device, + iface_desc, + hid_desc, + ep_in_desc), + "Unable to add HID Interface to the RAM list"); + } // USB_CLASS_HID + } + + hid_host_notify_interface_connected(hid_device); + + return ESP_OK; +} + +/** + * @brief HID Host initialize device attempt + * + * @param[in] dev_addr USB device physical address + * @return true USB device contain HID Interface and device was initialized + * @return false USB does not contain HID Interface + */ +static bool hid_host_device_init_attempt(uint8_t dev_addr) +{ + bool is_hid_device = false; + usb_device_handle_t dev_hdl; + const usb_config_desc_t *config_desc = NULL; + hid_device_t *hid_device = NULL; + + if (usb_host_device_open(s_hid_driver->client_handle, dev_addr, &dev_hdl) == ESP_OK) { + if (usb_host_get_active_config_descriptor(dev_hdl, &config_desc) == ESP_OK) { + is_hid_device = hid_interface_present(config_desc); + } + } + + // Create HID interfaces list in RAM, connected to the particular USB dev + if (is_hid_device) { + // Proceed, add HID device to the list, get handle if necessary + ESP_ERROR_CHECK( hid_host_install_device(dev_addr, dev_hdl, &hid_device) ); + // Create Interfaces list for a possibility to claim Interface + ESP_ERROR_CHECK( hid_host_interface_list_create(hid_device, config_desc) ); + } else { + usb_host_device_close(s_hid_driver->client_handle, dev_hdl); + ESP_LOGW(TAG, "No HID device at USB port %d", dev_addr); + } + + return is_hid_device; +} + +/** + * @brief USB device was removed we need to shutdown HID Interface + * + * @param[in] hid_dev_handle Handle of the HID devive to close + * @param[in] force To shutdown HID interface immediately + * @return esp_err_t + */ +static esp_err_t hid_host_interface_shutdown(hid_host_device_handle_t hid_dev_handle, + bool force) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + + iface->state = HID_INTERFACE_STATE_WAIT_USER_DELETION; + + // Not force shutdown and Interface was started by user + if (!force && iface->user_cb) { + hid_host_user_interface_callback(iface, HID_HOST_INTERFACE_EVENT_DISCONNECTED); + return ESP_OK; + } + + // Interface has not been opened, delete it right away + return hid_host_device_close(iface); +} + +/** + * @brief Destroy a list of available interfaces in RAM + * + * @return esp_err_t + */ +static esp_err_t hid_host_interface_list_destroy(void) +{ + bool active_shutdown = false; + + hid_iface_t *iface = STAILQ_FIRST(&s_hid_driver->hid_ifaces_tailq); + hid_iface_t *tmp = NULL; + + while (iface != NULL) { + tmp = STAILQ_NEXT(iface, tailq_entry); + // free interface + if (HID_INTERFACE_STATE_NOT_INITIALIZED != iface->state) { + active_shutdown = true; + ESP_ERROR_CHECK( hid_host_device_close(iface) ); + ESP_ERROR_CHECK( hid_host_interface_shutdown(iface, true)); + } + + HID_ENTER_CRITICAL(); + iface->state = HID_INTERFACE_STATE_NOT_INITIALIZED; + STAILQ_REMOVE(&s_hid_driver->hid_ifaces_tailq, iface, hid_interface, tailq_entry); + free(iface); + HID_EXIT_CRITICAL(); + + iface = tmp; + } + + if (active_shutdown) { + ESP_LOGE(TAG, "Shutdown active HID device."); + } + + return ESP_OK; +} + +/** + * @brief Deinit USB device by handle + * + * @param[in] dev_hdl USB device handle + * @return esp_err_t + */ +static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl) +{ + hid_device_t *hid_device = get_hid_device_by_handle(dev_hdl); + // Device should be in the list + assert(hid_device); + + HID_ENTER_CRITICAL(); + hid_iface_t *iface = STAILQ_FIRST(&s_hid_driver->hid_ifaces_tailq); + hid_iface_t *tmp = NULL; + while (iface != NULL) { + tmp = STAILQ_NEXT(iface, tailq_entry); + HID_EXIT_CRITICAL(); + if (iface->parent && (iface->parent->dev_addr == hid_device->dev_addr)) { + HID_RETURN_ON_ERROR( hid_host_device_close(iface), + "Unable to close device"); + HID_RETURN_ON_ERROR( hid_host_interface_shutdown(iface, false), + "Unable to shutdown interface"); + } + iface = tmp; + HID_ENTER_CRITICAL(); + } + HID_EXIT_CRITICAL(); + // Delete HID compliant device + HID_RETURN_ON_ERROR( hid_host_uninstall_device(hid_device), + "Unable to uninstall device"); + + return ESP_OK; +} + +/** + * @brief USB Host Client's event callback + * + * @param[in] event Client event message + * @param[in] arg Argument, does not used + */ +static void client_event_cb(const usb_host_client_event_msg_t *event, void *arg) +{ + if (event->event == USB_HOST_CLIENT_EVENT_NEW_DEV) { + hid_host_device_init_attempt(event->new_dev.address); + } else if (event->event == USB_HOST_CLIENT_EVENT_DEV_GONE) { + hid_host_device_disconnected(event->dev_gone.dev_hdl); + } +} + +/** + * @brief HID Host claim Interface and prepare transfer, change state to READY + * + * @param[in] iface Pointer to Interface structure, + * @return esp_err_t + */ +static esp_err_t hid_host_interface_claim_and_prepare_transfer(hid_iface_t *iface) +{ + HID_RETURN_ON_ERROR( usb_host_interface_claim( s_hid_driver->client_handle, + iface->parent->dev_hdl, + iface->dev_params.iface_num, 0), + "Unable to claim Interface"); + + HID_RETURN_ON_ERROR( usb_host_transfer_alloc(iface->ep_in_mps, 0, &iface->in_xfer), + "Unable to allocate transfer buffer for EP IN"); + + // Change state + iface->state = HID_INTERFACE_STATE_READY; + return ESP_OK; +} + +/** + * @brief HID Host release Interface and free transfer, change state to IDLE + * + * @param[in] iface Pointer to Interface structure, + * @return esp_err_t + */ +static esp_err_t hid_host_interface_release_and_free_transfer(hid_iface_t *iface) +{ + HID_RETURN_ON_INVALID_ARG(iface); + HID_RETURN_ON_INVALID_ARG(iface->parent); + + HID_RETURN_ON_FALSE(is_interface_in_list(iface), + ESP_ERR_NOT_FOUND, + "Interface handle not found"); + + HID_RETURN_ON_ERROR( usb_host_interface_release(s_hid_driver->client_handle, + iface->parent->dev_hdl, + iface->dev_params.iface_num), + "Unable to release HID Interface"); + + ESP_ERROR_CHECK( usb_host_transfer_free(iface->in_xfer) ); + + // Change state + iface->state = HID_INTERFACE_STATE_IDLE; + return ESP_OK; +} + +/** + * @brief Disable active interface + * + * @param[in] iface Pointer to Interface structure + * @return esp_err_t + */ +static esp_err_t hid_host_disable_interface(hid_iface_t *iface) +{ + HID_RETURN_ON_INVALID_ARG(iface); + HID_RETURN_ON_INVALID_ARG(iface->parent); + + HID_RETURN_ON_FALSE(is_interface_in_list(iface), + ESP_ERR_NOT_FOUND, + "Interface handle not found"); + + HID_RETURN_ON_FALSE((HID_INTERFACE_STATE_ACTIVE == iface->state), + ESP_ERR_INVALID_STATE, + "Interface wrong state"); + + HID_RETURN_ON_ERROR( usb_host_endpoint_halt(iface->parent->dev_hdl, iface->ep_in), + "Unable to HALT EP"); + HID_RETURN_ON_ERROR( usb_host_endpoint_flush(iface->parent->dev_hdl, iface->ep_in), + "Unable to FLUSH EP"); + usb_host_endpoint_clear(iface->parent->dev_hdl, iface->ep_in); + + iface->state = HID_INTERFACE_STATE_READY; + + return ESP_OK; +} + +/** + * @brief HID IN Transfer complete callback + * + * @param[in] transfer Pointer to transfer data structure + */ +static void in_xfer_done(usb_transfer_t *in_xfer) +{ + assert(in_xfer); + + hid_iface_t *iface = get_interface_by_ep(in_xfer->bEndpointAddress); + assert(iface); + + // Interfaces' parent device should be the same as the hid_device in context + assert(get_hid_device_from_context(in_xfer) == iface->parent); + + switch (in_xfer->status) { + case USB_TRANSFER_STATUS_COMPLETED: + // Notify user + hid_host_user_interface_callback(iface, HID_HOST_INTERFACE_EVENT_INPUT_REPORT); + // Relaunch transfer + usb_host_transfer_submit(in_xfer); + return; + case USB_TRANSFER_STATUS_NO_DEVICE: + case USB_TRANSFER_STATUS_CANCELED: + // User is notified about device disconnection from usb_event_cb + // No need to do anything + return; + default: + // Any other error + break; + } + + ESP_LOGE(TAG, "Transfer failed, status %d", in_xfer->status); + // Notify user about transfer or any other error + hid_host_user_interface_callback(iface, HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR); +} + +/** Lock HID device from other task + * + * @param[in] hid_device Pointer to HID device structure + * @param[in] timeout_ms Timeout of trying to take the mutex + * @return esp_err_t + */ +static inline esp_err_t hid_device_try_lock(hid_device_t *hid_device, uint32_t timeout_ms) +{ + return ( xSemaphoreTake(hid_device->device_busy, pdMS_TO_TICKS(timeout_ms)) + ? ESP_OK + : ESP_ERR_TIMEOUT ); +} + +/** Unlock HID device from other task + * + * @param[in] hid_device Pointer to HID device structure + * @param[in] timeout_ms Timeout of trying to take the mutex + * @return esp_err_t + */ +static inline void hid_device_unlock(hid_device_t *hid_device) +{ + xSemaphoreGive(hid_device->device_busy); +} + +/** + * @brief HID Control transfer complete callback + * + * @param[in] ctrl_xfer Pointer to transfer data structure + */ +static void ctrl_xfer_done(usb_transfer_t *ctrl_xfer) +{ + assert(ctrl_xfer); + hid_device_t *hid_device = (hid_device_t *)ctrl_xfer->context; + xSemaphoreGive(hid_device->ctrl_xfer_done); +} + +/** + * @brief HID control transfer synchronous. + * + * @note Passes interface and endpoint descriptors to obtain: + + * - interface number, IN endpoint, OUT endpoint, max. packet size + * + * @param[in] hid_device Pointer to HID device structure + * @param[in] ctrl_xfer Pointer to the Transfer structure + * @param[in] len Number of bytes to transfer + * @param[in] timeout_ms Timeout in ms + * @return esp_err_t + */ +static esp_err_t hid_control_transfer(hid_device_t *hid_device, + size_t len, + uint32_t timeout_ms) +{ + + usb_transfer_t *ctrl_xfer = hid_device->ctrl_xfer; + + ctrl_xfer->device_handle = hid_device->dev_hdl; + ctrl_xfer->callback = ctrl_xfer_done; + ctrl_xfer->context = hid_device; + ctrl_xfer->bEndpointAddress = 0; + ctrl_xfer->timeout_ms = timeout_ms; + ctrl_xfer->num_bytes = len; + + HID_RETURN_ON_ERROR( usb_host_transfer_submit_control(s_hid_driver->client_handle, ctrl_xfer), + "Unable to submit control transfer"); + + BaseType_t received = xSemaphoreTake(hid_device->ctrl_xfer_done, pdMS_TO_TICKS(ctrl_xfer->timeout_ms)); + + if (received != pdTRUE) { + // Transfer was not finished, error in USB LIB. Reset the endpoint + ESP_LOGE(TAG, "Control Transfer Timeout"); + + HID_RETURN_ON_ERROR( usb_host_endpoint_halt(hid_device->dev_hdl, ctrl_xfer->bEndpointAddress), + "Unable to HALT EP"); + HID_RETURN_ON_ERROR( usb_host_endpoint_flush(hid_device->dev_hdl, ctrl_xfer->bEndpointAddress), + "Unable to FLUSH EP"); + usb_host_endpoint_clear(hid_device->dev_hdl, ctrl_xfer->bEndpointAddress); + return ESP_ERR_TIMEOUT; + } + + ESP_LOG_BUFFER_HEXDUMP(TAG, ctrl_xfer->data_buffer, ctrl_xfer->actual_num_bytes, ESP_LOG_DEBUG); + + return ESP_OK; +} + +/** + * @brief USB class standard request get descriptor + * + * @param[in] hidh_device Pointer to HID device structure + * @param[in] req Pointer to a class specific request structure + * @return esp_err_t + */ +static esp_err_t usb_class_request_get_descriptor(hid_device_t *hid_device, const hid_class_request_t *req) +{ + esp_err_t ret; + usb_transfer_t *ctrl_xfer = hid_device->ctrl_xfer; + const size_t ctrl_size = hid_device->ctrl_xfer->data_buffer_size; + + HID_RETURN_ON_INVALID_ARG(hid_device); + HID_RETURN_ON_INVALID_ARG(hid_device->ctrl_xfer); + HID_RETURN_ON_INVALID_ARG(req); + HID_RETURN_ON_INVALID_ARG(req->data); + + HID_RETURN_ON_ERROR( hid_device_try_lock(hid_device, DEFAULT_TIMEOUT_MS), + "HID Device is busy by other task"); + + if (ctrl_size < (USB_SETUP_PACKET_SIZE + req->wLength)) { + usb_device_info_t dev_info; + ESP_ERROR_CHECK(usb_host_device_info(hid_device->dev_hdl, &dev_info)); + // reallocate the ctrl xfer buffer for new length + ESP_LOGI(TAG, "Change HID ctrl xfer size from %d to %d", + ctrl_size, + (int) (USB_SETUP_PACKET_SIZE + req->wLength)); + + usb_host_transfer_free(hid_device->ctrl_xfer); + HID_RETURN_ON_ERROR( usb_host_transfer_alloc(USB_SETUP_PACKET_SIZE + req->wLength, + 0, + &hid_device->ctrl_xfer), + "Unable to allocate transfer buffer for EP0"); + } + + usb_setup_packet_t *setup = (usb_setup_packet_t *)ctrl_xfer->data_buffer; + + setup->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | + USB_BM_REQUEST_TYPE_TYPE_STANDARD | + USB_BM_REQUEST_TYPE_RECIP_INTERFACE; + setup->bRequest = req->bRequest; + setup->wValue = req->wValue; + setup->wIndex = req->wIndex; + setup->wLength = req->wLength; + + ret = hid_control_transfer(hid_device, + USB_SETUP_PACKET_SIZE + req->wLength, + DEFAULT_TIMEOUT_MS); + + if (ESP_OK == ret) { + ctrl_xfer->actual_num_bytes -= USB_SETUP_PACKET_SIZE; + if (ctrl_xfer->actual_num_bytes <= req->wLength) { + memcpy(req->data, ctrl_xfer->data_buffer + USB_SETUP_PACKET_SIZE, ctrl_xfer->actual_num_bytes); + } else { + ret = ESP_ERR_INVALID_SIZE; + } + } + + hid_device_unlock(hid_device); + + return ret; +} + +/** + * @brief HID Host Request Report Descriptor + * + * @param[in] hidh_iface Pointer to HID Interface configuration structure + * @return esp_err_t + */ +static esp_err_t hid_class_request_report_descriptor(hid_iface_t *iface) +{ + HID_RETURN_ON_INVALID_ARG(iface); + + // Get Report Descritpor is possible only in Ready or Active state + HID_RETURN_ON_FALSE((HID_INTERFACE_STATE_READY == iface->state) || + (HID_INTERFACE_STATE_ACTIVE == iface->state), + ESP_ERR_INVALID_STATE, + "Unable to request report decriptor. Interface is not ready"); + + iface->report_desc = malloc(iface->report_desc_size); + HID_RETURN_ON_FALSE(iface->report_desc, + ESP_ERR_NO_MEM, + "Unable to allocate memory"); + + const hid_class_request_t get_desc = { + .bRequest = USB_B_REQUEST_GET_DESCRIPTOR, + .wValue = (HID_CLASS_DESCRIPTOR_TYPE_REPORT << 8), + .wIndex = iface->dev_params.iface_num, + .wLength = iface->report_desc_size, + .data = iface->report_desc + }; + + return usb_class_request_get_descriptor(iface->parent, &get_desc); +} + +/** + * @brief HID class specific request Set + * + * @param[in] hid_device Pointer to HID device structure + * @param[in] req Pointer to a class specific request structure + * @return esp_err_t + */ +static esp_err_t hid_class_request_set(hid_device_t *hid_device, + const hid_class_request_t *req) +{ + esp_err_t ret; + usb_transfer_t *ctrl_xfer = hid_device->ctrl_xfer; + HID_RETURN_ON_INVALID_ARG(hid_device); + HID_RETURN_ON_INVALID_ARG(hid_device->ctrl_xfer); + + HID_RETURN_ON_ERROR( hid_device_try_lock(hid_device, DEFAULT_TIMEOUT_MS), + "HID Device is busy by other task"); + + usb_setup_packet_t *setup = (usb_setup_packet_t *)ctrl_xfer->data_buffer; + setup->bmRequestType = USB_BM_REQUEST_TYPE_DIR_OUT | + USB_BM_REQUEST_TYPE_TYPE_CLASS | + USB_BM_REQUEST_TYPE_RECIP_INTERFACE; + setup->bRequest = req->bRequest; + setup->wValue = req->wValue; + setup->wIndex = req->wIndex; + setup->wLength = req->wLength; + + if (req->wLength && req->data) { + memcpy(ctrl_xfer->data_buffer + USB_SETUP_PACKET_SIZE, req->data, req->wLength); + } + + ret = hid_control_transfer(hid_device, + USB_SETUP_PACKET_SIZE + setup->wLength, + DEFAULT_TIMEOUT_MS); + + hid_device_unlock(hid_device); + + return ret; +} + +/** + * @brief HID class specific request Get + * + * @param[in] hid_device Pointer to HID device structure + * @param[in] req Pointer to a class specific request structure + * @param[out] out_length Length of the response in data buffer of req struct + * @return esp_err_t + */ +static esp_err_t hid_class_request_get(hid_device_t *hid_device, + const hid_class_request_t *req, + size_t *out_length) +{ + esp_err_t ret; + HID_RETURN_ON_INVALID_ARG(hid_device); + HID_RETURN_ON_INVALID_ARG(hid_device->ctrl_xfer); + + usb_transfer_t *ctrl_xfer = hid_device->ctrl_xfer; + + HID_RETURN_ON_ERROR( hid_device_try_lock(hid_device, DEFAULT_TIMEOUT_MS), + "HID Device is busy by other task"); + + usb_setup_packet_t *setup = (usb_setup_packet_t *)ctrl_xfer->data_buffer; + + setup->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | + USB_BM_REQUEST_TYPE_TYPE_CLASS | + USB_BM_REQUEST_TYPE_RECIP_INTERFACE; + setup->bRequest = req->bRequest; + setup->wValue = req->wValue; + setup->wIndex = req->wIndex; + setup->wLength = req->wLength; + + ret = hid_control_transfer(hid_device, + USB_SETUP_PACKET_SIZE + setup->wLength, + DEFAULT_TIMEOUT_MS); + + if (ESP_OK == ret) { + // We do not need the setup data, which is still in the transfer data buffer + ctrl_xfer->actual_num_bytes -= USB_SETUP_PACKET_SIZE; + // Copy data if the size is ok + if (ctrl_xfer->actual_num_bytes <= req->wLength) { + memcpy(req->data, ctrl_xfer->data_buffer + USB_SETUP_PACKET_SIZE, ctrl_xfer->actual_num_bytes); + // return actual num bytes of response + if (out_length) { + *out_length = ctrl_xfer->actual_num_bytes; + } + } else { + ret = ESP_ERR_INVALID_SIZE; + } + } + + hid_device_unlock(hid_device); + + return ret; +} + +// ---------------------------- Private --------------------------------------- + +esp_err_t hid_host_install_device(uint8_t dev_addr, + usb_device_handle_t dev_hdl, + hid_device_t **hid_device_handle) +{ + esp_err_t ret; + hid_device_t *hid_device; + + HID_GOTO_ON_FALSE( hid_device = calloc(1, sizeof(hid_device_t)), + ESP_ERR_NO_MEM, + "Unable to allocate memory for HID Device"); + + hid_device->dev_addr = dev_addr; + hid_device->dev_hdl = dev_hdl; + + HID_GOTO_ON_FALSE( hid_device->ctrl_xfer_done = xSemaphoreCreateBinary(), + ESP_ERR_NO_MEM, + "Unable to create semaphore"); + HID_GOTO_ON_FALSE( hid_device->device_busy = xSemaphoreCreateMutex(), + ESP_ERR_NO_MEM, + "Unable to create semaphore"); + + /* + * TIP: Usually, we need to allocate 'EP bMaxPacketSize0 + 1' here. + * To take the size of a report descriptor into a consideration, + * we need to allocate more here, e.g. 512 bytes. + */ + HID_GOTO_ON_ERROR(usb_host_transfer_alloc(512, 0, &hid_device->ctrl_xfer), + "Unable to allocate transfer buffer"); + + HID_ENTER_CRITICAL(); + HID_GOTO_ON_FALSE_CRITICAL( s_hid_driver, ESP_ERR_INVALID_STATE ); + HID_GOTO_ON_FALSE_CRITICAL( s_hid_driver->client_handle, ESP_ERR_INVALID_STATE ); + STAILQ_INSERT_TAIL(&s_hid_driver->hid_devices_tailq, hid_device, tailq_entry); + HID_EXIT_CRITICAL(); + + if (hid_device_handle) { + *hid_device_handle = hid_device; + } + + return ESP_OK; + +fail: + hid_host_uninstall_device(hid_device); + return ret; +} + +esp_err_t hid_host_uninstall_device(hid_device_t *hid_device) +{ + HID_RETURN_ON_INVALID_ARG(hid_device); + + HID_RETURN_ON_ERROR( usb_host_transfer_free(hid_device->ctrl_xfer), + "Unablet to free transfer buffer for EP0"); + HID_RETURN_ON_ERROR( usb_host_device_close(s_hid_driver->client_handle, + hid_device->dev_hdl), + "Unable to close USB host"); + + if (hid_device->ctrl_xfer_done) { + vSemaphoreDelete(hid_device->ctrl_xfer_done); + } + + if (hid_device->device_busy) { + vSemaphoreDelete(hid_device->device_busy); + } + + HID_ENTER_CRITICAL(); + STAILQ_REMOVE(&s_hid_driver->hid_devices_tailq, hid_device, hid_host_device, tailq_entry); + HID_EXIT_CRITICAL(); + + free(hid_device); + return ESP_OK; +} + +// ----------------------------- Public ---------------------------------------- + +esp_err_t hid_host_install(const hid_host_driver_config_t *config) +{ + esp_err_t ret; + + HID_RETURN_ON_INVALID_ARG(config); + HID_RETURN_ON_INVALID_ARG(config->callback); + + if ( config->create_background_task ) { + HID_RETURN_ON_FALSE(config->stack_size != 0, + ESP_ERR_INVALID_ARG, + "Wrong stack size value"); + HID_RETURN_ON_FALSE(config->task_priority != 0, + ESP_ERR_INVALID_ARG, + "Wrong task priority value"); + } + + HID_RETURN_ON_FALSE(!s_hid_driver, + ESP_ERR_INVALID_STATE, + "HID Host driver is already installed"); + + // Create HID driver structure + hid_driver_t *driver = heap_caps_calloc(1, sizeof(hid_driver_t), MALLOC_CAP_DEFAULT); + HID_RETURN_ON_FALSE(driver, + ESP_ERR_NO_MEM, + "Unable to allocate memory"); + + driver->user_cb = config->callback; + driver->user_arg = config->callback_arg; + + usb_host_client_config_t client_config = { + .is_synchronous = false, + .async.client_event_callback = client_event_cb, + .async.callback_arg = NULL, + .max_num_event_msg = 10, + }; + + driver->end_client_event_handling = false; + driver->all_events_handled = xSemaphoreCreateBinary(); + HID_GOTO_ON_FALSE(driver->all_events_handled, + ESP_ERR_NO_MEM, + "Unable to create semaphore"); + + HID_GOTO_ON_ERROR( usb_host_client_register(&client_config, + &driver->client_handle), + "Unable to register USB Host client"); + + HID_ENTER_CRITICAL(); + HID_GOTO_ON_FALSE_CRITICAL(!s_hid_driver, ESP_ERR_INVALID_STATE); + s_hid_driver = driver; + STAILQ_INIT(&s_hid_driver->hid_devices_tailq); + STAILQ_INIT(&s_hid_driver->hid_ifaces_tailq); + HID_EXIT_CRITICAL(); + + if (config->create_background_task) { + BaseType_t task_created = xTaskCreatePinnedToCore( + event_handler_task, + "USB HID Host", + config->stack_size, + NULL, + config->task_priority, + NULL, + config->core_id); + HID_GOTO_ON_FALSE(task_created, + ESP_ERR_NO_MEM, + "Unable to create USB HID Host task"); + } + + return ESP_OK; + +fail: + s_hid_driver = NULL; + if (driver->client_handle) { + usb_host_client_deregister(driver->client_handle); + } + if (driver->all_events_handled) { + vSemaphoreDelete(driver->all_events_handled); + } + free(driver); + return ret; +} + +esp_err_t hid_host_uninstall(void) +{ + // Make sure hid driver is installed, + HID_RETURN_ON_FALSE(s_hid_driver, + ESP_OK, + "HID Host driver was not installed"); + + // Flush Interface list if needed + HID_RETURN_ON_ERROR(hid_host_interface_list_destroy(), + "Unable to flush Interfaces list"); + + // Make sure that hid driver + // not being uninstalled from other task + // and no hid device is registered + HID_ENTER_CRITICAL(); + HID_RETURN_ON_FALSE_CRITICAL( !s_hid_driver->end_client_event_handling, ESP_ERR_INVALID_STATE ); + HID_RETURN_ON_FALSE_CRITICAL( STAILQ_EMPTY(&s_hid_driver->hid_devices_tailq), ESP_ERR_INVALID_STATE ); + HID_RETURN_ON_FALSE_CRITICAL( STAILQ_EMPTY(&s_hid_driver->hid_ifaces_tailq), ESP_ERR_INVALID_STATE ); + s_hid_driver->end_client_event_handling = true; + HID_EXIT_CRITICAL(); + + ESP_ERROR_CHECK( usb_host_client_unblock(s_hid_driver->client_handle) ); + xSemaphoreTake(s_hid_driver->all_events_handled, portMAX_DELAY); + vSemaphoreDelete(s_hid_driver->all_events_handled); + free(s_hid_driver); + s_hid_driver = NULL; + return ESP_OK; +} + +esp_err_t hid_host_device_open(hid_host_device_handle_t hid_dev_handle, + const hid_host_device_config_t *config) +{ + HID_RETURN_ON_FALSE(s_hid_driver, + ESP_ERR_INVALID_STATE, + "HID Driver is not installed"); + + hid_iface_t *hid_iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(hid_iface); + + HID_RETURN_ON_FALSE((hid_iface->dev_params.proto >= HID_PROTOCOL_NONE) + && (hid_iface->dev_params.proto < HID_PROTOCOL_MAX), + ESP_ERR_INVALID_ARG, + "HID device protocol not supported"); + + HID_RETURN_ON_FALSE((HID_INTERFACE_STATE_IDLE == hid_iface->state), + ESP_ERR_INVALID_STATE, + "Interface wrong state"); + + // Claim interface, allocate xfer and save report callback + HID_RETURN_ON_ERROR( hid_host_interface_claim_and_prepare_transfer(hid_iface), + "Unable to claim interface"); + + // Save HID Interface callback + hid_iface->user_cb = config->callback; + hid_iface->user_cb_arg = config->callback_arg; + + return ESP_OK; +} + +esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle) +{ + hid_iface_t *hid_iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(hid_iface); + + if (HID_INTERFACE_STATE_ACTIVE == hid_iface->state) { + HID_RETURN_ON_ERROR( hid_host_disable_interface(hid_iface), + "Unable to disable HID Interface"); + } + + if (HID_INTERFACE_STATE_READY == hid_iface->state) { + HID_RETURN_ON_ERROR( hid_host_interface_release_and_free_transfer(hid_iface), + "Unable to release HID Interface"); + + // If the device is closing by user before device detached we need to flush user callback here + free(hid_iface->report_desc); + hid_iface->report_desc = NULL; + } + + if (HID_INTERFACE_STATE_WAIT_USER_DELETION == hid_iface->state) { + hid_iface->user_cb = NULL; + hid_iface->user_cb_arg = NULL; + + /* Remove Interface from the list */ + HID_ENTER_CRITICAL(); + hid_iface->state = HID_INTERFACE_STATE_NOT_INITIALIZED; + STAILQ_REMOVE(&s_hid_driver->hid_ifaces_tailq, hid_iface, hid_interface, tailq_entry); + HID_EXIT_CRITICAL(); + free(hid_iface); + } + + return ESP_OK; +} + +esp_err_t hid_host_handle_events(uint32_t timeout_ms) +{ + HID_RETURN_ON_FALSE(s_hid_driver, + ESP_ERR_INVALID_STATE, + "HID Driver is not installed"); + + return usb_host_client_handle_events(s_hid_driver->client_handle, timeout_ms); +} + +esp_err_t hid_host_device_get_params(hid_host_device_handle_t hid_dev_handle, + hid_host_dev_params_t *dev_params) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_FALSE(iface, + ESP_ERR_INVALID_STATE, + "HID Interface not found"); + + HID_RETURN_ON_FALSE(dev_params, + ESP_ERR_INVALID_ARG, + "Wrong argument"); + + memcpy(dev_params, &iface->dev_params, sizeof(hid_host_dev_params_t)); + return ESP_OK; +} +esp_err_t hid_host_device_get_raw_input_report_data(hid_host_device_handle_t hid_dev_handle, + uint8_t *data, + size_t data_length_max, + size_t *data_length) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_FALSE(iface, + ESP_ERR_INVALID_STATE, + "HID Interface not found"); + + HID_RETURN_ON_FALSE(data, + ESP_ERR_INVALID_ARG, + "Wrong argument"); + + HID_RETURN_ON_FALSE(data_length, + ESP_ERR_INVALID_ARG, + "Wrong argument"); + + size_t copied = (data_length_max >= iface->in_xfer->actual_num_bytes) + ? iface->in_xfer->actual_num_bytes + : data_length_max; + memcpy(data, iface->in_xfer->data_buffer, copied); + *data_length = copied; + return ESP_OK; +} + +// ------------------------ USB HID Host driver API ---------------------------- + +esp_err_t hid_host_device_start(hid_host_device_handle_t hid_dev_handle) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + HID_RETURN_ON_INVALID_ARG(iface->in_xfer); + HID_RETURN_ON_INVALID_ARG(iface->parent); + + HID_RETURN_ON_FALSE(is_interface_in_list(iface), + ESP_ERR_NOT_FOUND, + "Interface handle not found"); + + HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_READY == iface->state), + ESP_ERR_INVALID_STATE, + "Interface wrong state"); + + // prepare transfer + iface->in_xfer->device_handle = iface->parent->dev_hdl; + iface->in_xfer->callback = in_xfer_done; + iface->in_xfer->context = iface->parent; + iface->in_xfer->timeout_ms = DEFAULT_TIMEOUT_MS; + iface->in_xfer->bEndpointAddress = iface->ep_in; + iface->in_xfer->num_bytes = iface->ep_in_mps; + + iface->state = HID_INTERFACE_STATE_ACTIVE; + + // start data transfer + return usb_host_transfer_submit(iface->in_xfer); +} + +esp_err_t hid_host_device_stop(hid_host_device_handle_t hid_dev_handle) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + + return hid_host_disable_interface(iface); +} + +uint8_t *hid_host_get_report_descriptor(hid_host_device_handle_t hid_dev_handle, + size_t *report_desc_len) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + if (NULL == iface) { + return NULL; + } + + // Report Descriptor was already requested, return pointer + if (iface->report_desc) { + *report_desc_len = iface->report_desc_size; + return iface->report_desc; + } + + // Request Report Descriptor + if (ESP_OK == hid_class_request_report_descriptor(iface)) { + *report_desc_len = iface->report_desc_size; + return iface->report_desc; + } + + return NULL; +} + +esp_err_t hid_class_request_get_report(hid_host_device_handle_t hid_dev_handle, + uint8_t report_type, + uint8_t report_id, + uint8_t *report, + size_t *report_length) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + HID_RETURN_ON_INVALID_ARG(report); + + const hid_class_request_t get_report = { + .bRequest = HID_CLASS_SPECIFIC_REQ_GET_REPORT, + .wValue = (report_type << 8) | report_id, + .wIndex = iface->dev_params.iface_num, + .wLength = *report_length, + .data = report + }; + + return hid_class_request_get(iface->parent, &get_report, report_length); +} + +esp_err_t hid_class_request_get_idle(hid_host_device_handle_t hid_dev_handle, + uint8_t report_id, + uint8_t *idle_rate) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + HID_RETURN_ON_INVALID_ARG(idle_rate); + + uint8_t tmp[1] = { 0xff }; + + const hid_class_request_t get_idle = { + .bRequest = HID_CLASS_SPECIFIC_REQ_GET_IDLE, + .wValue = report_id, + .wIndex = iface->dev_params.iface_num, + .wLength = 1, + .data = tmp + }; + + HID_RETURN_ON_ERROR( hid_class_request_get(iface->parent, &get_idle, NULL), + "HID class request transfer failure"); + + *idle_rate = tmp[0]; + + return ESP_OK; +} + +esp_err_t hid_class_request_get_protocol(hid_host_device_handle_t hid_dev_handle, + hid_report_protocol_t *protocol) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + HID_RETURN_ON_INVALID_ARG(protocol); + + uint8_t tmp[1] = { 0xff }; + + const hid_class_request_t get_proto = { + .bRequest = HID_CLASS_SPECIFIC_REQ_GET_PROTOCOL, + .wValue = 0, + .wIndex = iface->dev_params.iface_num, + .wLength = 1, + .data = tmp + }; + + HID_RETURN_ON_ERROR( hid_class_request_get(iface->parent, &get_proto, NULL), + "HID class request failure"); + + *protocol = (hid_report_protocol_t) tmp[0]; + return ESP_OK; +} + +esp_err_t hid_class_request_set_report(hid_host_device_handle_t hid_dev_handle, + uint8_t report_type, + uint8_t report_id, + uint8_t *report, + size_t report_length) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + + const hid_class_request_t set_report = { + .bRequest = HID_CLASS_SPECIFIC_REQ_SET_REPORT, + .wValue = (report_type << 8) | report_id, + .wIndex = iface->dev_params.iface_num, + .wLength = report_length, + .data = report + }; + + return hid_class_request_set(iface->parent, &set_report); +} + +esp_err_t hid_class_request_set_idle(hid_host_device_handle_t hid_dev_handle, + uint8_t duration, + uint8_t report_id) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + + const hid_class_request_t set_idle = { + .bRequest = HID_CLASS_SPECIFIC_REQ_SET_IDLE, + .wValue = (duration << 8) | report_id, + .wIndex = iface->dev_params.iface_num, + .wLength = 0, + .data = NULL + }; + + return hid_class_request_set(iface->parent, &set_idle); +} + +esp_err_t hid_class_request_set_protocol(hid_host_device_handle_t hid_dev_handle, + hid_report_protocol_t protocol) +{ + hid_iface_t *iface = get_iface_by_handle(hid_dev_handle); + + HID_RETURN_ON_INVALID_ARG(iface); + + const hid_class_request_t set_proto = { + .bRequest = HID_CLASS_SPECIFIC_REQ_SET_PROTOCOL, + .wValue = protocol, + .wIndex = iface->dev_params.iface_num, + .wLength = 0, + .data = NULL + }; + + return hid_class_request_set(iface->parent, &set_proto); +} diff --git a/examples/USB_Host_Keyboard_Mouse/src/hid_host.h b/examples/USB_Host_Keyboard_Mouse/src/hid_host.h new file mode 100644 index 0000000..6898b3e --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/src/hid_host.h @@ -0,0 +1,280 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "esp_err.h" +#include + +#include "hid.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct hid_interface *hid_host_device_handle_t; /**< Device Handle. Handle to a particular HID interface */ + +// ------------------------ USB HID Host events -------------------------------- +/** + * @brief USB HID HOST Device event id +*/ +typedef enum { + HID_HOST_DRIVER_EVENT_CONNECTED = 0x00, /**< HID Device has been found in connected USB device (at least one) */ +} hid_host_driver_event_t; + +/** + * @brief USB HID HOST Interface event id +*/ +typedef enum { + HID_HOST_INTERFACE_EVENT_INPUT_REPORT = 0x00, /**< HID Device input report */ + HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR, /**< HID Device transfer error */ + HID_HOST_INTERFACE_EVENT_DISCONNECTED, /**< HID Device has been disconnected */ +} hid_host_interface_event_t; + +/** + * @brief USB HID Host device parameters +*/ +typedef struct { + uint8_t addr; /**< USB Address of connected HID device.*/ + uint8_t iface_num; /**< HID Interface Number */ + uint8_t sub_class; /**< HID Interface SubClass */ + uint8_t proto; /**< HID Interface Protocol */ +} hid_host_dev_params_t; + +// ------------------------ USB HID Host callbacks ----------------------------- + +/** + * @brief USB HID driver event callback. + * + * @param[in] hid_handle HID device handle (HID Interface) + * @param[in] event HID driver event + * @param[in] arg User argument from HID driver configuration structure +*/ +typedef void (*hid_host_driver_event_cb_t)(hid_host_device_handle_t hid_device_handle, + const hid_host_driver_event_t event, + void *arg); + +/** + * @brief USB HID Interface event callback. + * + * @param[in] hid_device_handle HID device handle (HID Interface) + * @param[in] event HID Interface event + * @param[in] arg User argument +*/ +typedef void (*hid_host_interface_event_cb_t)(hid_host_device_handle_t hid_device_handle, + const hid_host_interface_event_t event, + void *arg); + +// ----------------------------- Public --------------------------------------- +/** + * @brief HID configuration structure. +*/ +typedef struct { + bool create_background_task; /**< When set to true, background task handling USB events is created. + Otherwise user has to periodically call hid_host_handle_events function */ + size_t task_priority; /**< Task priority of created background task */ + size_t stack_size; /**< Stack size of created background task */ + BaseType_t core_id; /**< Select core on which background task will run or tskNO_AFFINITY */ + hid_host_driver_event_cb_t callback; /**< Callback invoked when HID driver event occurs. Must not be NULL. */ + void *callback_arg; /**< User provided argument passed to callback */ +} hid_host_driver_config_t; + +/** + * @brief HID device configuration structure (HID Interface) +*/ +typedef struct { + hid_host_interface_event_cb_t callback; /**< Callback invoked when HID Interface event occurs */ + void *callback_arg; /**< User provided argument passed to callback */ +} hid_host_device_config_t; + +/** + * @brief USB HID Host install USB Host HID Class driver + * + * @param[in] config configuration structure HID to create + * @return esp_err_r + */ +esp_err_t hid_host_install(const hid_host_driver_config_t *config); + +/** + * @brief USB HID Host uninstall HID Class driver + * @return esp_err_t + */ +esp_err_t hid_host_uninstall(void); + +/** + * @brief USB HID Host open a device with specific device parameters + * + * @param[in] iface_handle Handle of the HID devive to open + * @param[in] config Configuration structure HID device to open + * @return esp_err_t + */ +esp_err_t hid_host_device_open(hid_host_device_handle_t hid_dev_handle, + const hid_host_device_config_t *config); + +/** + * @brief USB HID Host close device + * + * @param[in] hid_dev_handle Handle of the HID devive to close + * @return esp_err_t + */ +esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle); + +/** + * @brief HID Host USB event handler + * + * If HID Host install was made with create_background_task=false configuration, + * application needs to handle USB Host events itself. + * Do not used if HID host install was made with create_background_task=true configuration + * + * @param[in] timeout_ms Timeout in milliseconds + * @return esp_err_t + */ +esp_err_t hid_host_handle_events(uint32_t timeout_ms); + +/** + * @brief HID Device get parameters by handle. + * + * @param[in] hid_dev_handle HID Device handle + * @param[out] dev_params Pointer to a dev_params struct to fill + * + * @return esp_err_t + */ +esp_err_t hid_host_device_get_params(hid_host_device_handle_t hid_dev_handle, + hid_host_dev_params_t *dev_params); +/** + * @brief HID Host get device raw input report data pointer by handle + * + * This functions should be called after HID Interface device event HID_HOST_INTERFACE_EVENT_INPUT_REPORT + * to get the actual raw data of input report. + * + * @param[in] hid_dev_handle HID Device handle + * @param[in] data Pointer to buffer where the input data will be copied + * @param[in] data_length_max Max length of data can be copied to data buffer + * @param[out] data_length Length of input report + * + * @return esp_err_t + */ +esp_err_t hid_host_device_get_raw_input_report_data(hid_host_device_handle_t hid_dev_handle, + uint8_t *data, + size_t data_length_max, + size_t *data_length); + +// ------------------------ USB HID Host driver API ---------------------------- + +/** + * @brief HID Host start awaiting event from a device by handle + * + * Calls a callback when the HID Interface event has occurred. + * + * @param[in] hid_dev_handle HID Device handle + * @return esp_err_t + */ +esp_err_t hid_host_device_start(hid_host_device_handle_t hid_dev_handle); + +/** + * @brief HID Host stop device + * + * @param[in] hid_dev_handle HID Device handle + * + * @return esp_err_t + */ +esp_err_t hid_host_device_stop(hid_host_device_handle_t hid_dev_handle); + +/** + * @brief HID Host Get Report Descriptor + * + * @param[in] hid_dev_handle HID Device handle + * @param[out] report_desc_len Length of report descriptor + * + * @return a uint8_t pointer to report descriptor data + */ +uint8_t *hid_host_get_report_descriptor(hid_host_device_handle_t hid_dev_handle, + size_t *report_desc_len); + +/** + * @brief HID class specific request GET REPORT + * + * @param[in] hid_dev_handle HID Device handle + * @param[in] report_id Report ID + * @param[out] report Pointer to buffer for a report data + * @param[in/out] report_length Report data length, before the get report contain the maximum value of a report buffer. + * After get report there is a value of actual data in report buffer. + * + * @return esp_err_t + */ +esp_err_t hid_class_request_get_report(hid_host_device_handle_t hid_dev_handle, + uint8_t report_type, + uint8_t report_id, + uint8_t *report, + size_t *report_length); + +/** + * @brief HID class specific request GET IDLE + * + * @param[in] hid_dev_handle HID Device handle + * @param[in] report_id ReportID + * @param[out] idle_rate Idle rate [ms] + * + * @return esp_err_t + */ +esp_err_t hid_class_request_get_idle(hid_host_device_handle_t hid_dev_handle, + uint8_t report_id, + uint8_t *idle_rate); + +/** + * @brief HID class specific request GET PROTOCOL + * + * @param[in] hid_dev_handle HID Device handle + * @param[out] protocol Pointer to HID report protocol (boot or report) of device + * + * @return esp_err_t + */ +esp_err_t hid_class_request_get_protocol(hid_host_device_handle_t hid_dev_handle, + hid_report_protocol_t *protocol); + +/** +* @brief HID class specific request SET REPORT +* +* @param[in] hid_dev_handle HID Device handle +* @param[in] report_type Report type +* @param[in] report_id Report ID +* @param[in] report Pointer to a buffer with report data +* @param[in] report_length Report data length +* +* @return esp_err_t +*/ +esp_err_t hid_class_request_set_report(hid_host_device_handle_t hid_dev_handle, + uint8_t report_type, + uint8_t report_id, + uint8_t *report, + size_t report_length); + +/** + * @brief HID class specific request SET IDLE + * + * @param[in] hid_dev_handle HID Device handle + * @param[in] duration 0 (zero) for the indefinite duration, non-zero, then a fixed duration used. + * @param[in] report_id If 0 (zero) the idle rate applies to all input reports generated by the device, otherwise ReportID + * @return esp_err_t + */ +esp_err_t hid_class_request_set_idle(hid_host_device_handle_t hid_dev_handle, + uint8_t duration, + uint8_t report_id); + +/** + * @brief HID class specific request SET PROTOCOL + * + * @param[in] hid_dev_handle HID Device handle + * @param[in] protocol HID report protocol (boot or report) + * @return esp_err_t + */ +esp_err_t hid_class_request_set_protocol(hid_host_device_handle_t hid_dev_handle, + hid_report_protocol_t protocol); + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/examples/USB_Host_Keyboard_Mouse/src/hid_usage_keyboard.h b/examples/USB_Host_Keyboard_Mouse/src/hid_usage_keyboard.h new file mode 100644 index 0000000..a740b90 --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/src/hid_usage_keyboard.h @@ -0,0 +1,292 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//------------------------------------------ HID usage keys --------------------------------------------------------------- +/** + * @brief HID Keys + * + */ +typedef enum { + HID_KEY_NO_PRESS = 0x00, + HID_KEY_ROLLOVER = 0x01, + HID_KEY_POST_FAIL = 0x02, + HID_KEY_ERROR_UNDEFINED = 0x03, + HID_KEY_A = 0x04, + HID_KEY_B = 0x05, + HID_KEY_C = 0x06, + HID_KEY_D = 0x07, + HID_KEY_E = 0x08, + HID_KEY_F = 0x09, + HID_KEY_G = 0x0A, + HID_KEY_H = 0x0B, + HID_KEY_I = 0x0C, + HID_KEY_J = 0x0D, + HID_KEY_K = 0x0E, + HID_KEY_L = 0x0F, + HID_KEY_M = 0x10, + HID_KEY_N = 0x11, + HID_KEY_O = 0x12, + HID_KEY_P = 0x13, + HID_KEY_Q = 0x14, + HID_KEY_R = 0x15, + HID_KEY_S = 0x16, + HID_KEY_T = 0x17, + HID_KEY_U = 0x18, + HID_KEY_V = 0x19, + HID_KEY_W = 0x1A, + HID_KEY_X = 0x1B, + HID_KEY_Y = 0x1C, + HID_KEY_Z = 0x1D, + HID_KEY_1 = 0x1E, + HID_KEY_2 = 0x1F, + HID_KEY_3 = 0x20, + HID_KEY_4 = 0x21, + HID_KEY_5 = 0x22, + HID_KEY_6 = 0x23, + HID_KEY_7 = 0x24, + HID_KEY_8 = 0x25, + HID_KEY_9 = 0x26, + HID_KEY_0 = 0x27, + HID_KEY_ENTER = 0x28, + HID_KEY_ESC = 0x29, + HID_KEY_DEL = 0x2A, + HID_KEY_TAB = 0x2B, + HID_KEY_SPACE = 0x2C, + HID_KEY_MINUS = 0x2D, + HID_KEY_EQUAL = 0x2E, + HID_KEY_OPEN_BRACKET = 0x2F, + HID_KEY_CLOSE_BRACKET = 0x30, + HID_KEY_BACK_SLASH = 0x31, + HID_KEY_SHARP = 0x32, + HID_KEY_COLON = 0x33, + HID_KEY_QUOTE = 0x34, + HID_KEY_TILDE = 0x35, + HID_KEY_LESS = 0x36, + HID_KEY_GREATER = 0x37, + HID_KEY_SLASH = 0x38, + HID_KEY_CAPS_LOCK = 0x39, + HID_KEY_F1 = 0x3A, + HID_KEY_F2 = 0x3B, + HID_KEY_F3 = 0x3C, + HID_KEY_F4 = 0x3D, + HID_KEY_F5 = 0x3E, + HID_KEY_F6 = 0x3F, + HID_KEY_F7 = 0x40, + HID_KEY_F8 = 0x41, + HID_KEY_F9 = 0x42, + HID_KEY_F10 = 0x43, + HID_KEY_F11 = 0x44, + HID_KEY_F12 = 0x45, + HID_KEY_PRINT_SCREEN = 0x46, + HID_KEY_SCROLL_LOCK = 0x47, + HID_KEY_PAUSE = 0x48, + HID_KEY_INSERT = 0x49, + HID_KEY_HOME = 0x4A, + HID_KEY_PAGEUP = 0x4B, + HID_KEY_DELETE = 0x4C, + HID_KEY_END = 0x4D, + HID_KEY_PAGEDOWN = 0x4E, + HID_KEY_RIGHT = 0x4F, + HID_KEY_LEFT = 0x50, + HID_KEY_DOWN = 0x51, + HID_KEY_UP = 0x52, + HID_KEY_NUM_LOCK = 0x53, + HID_KEY_KEYPAD_DIV = 0x54, + HID_KEY_KEYPAD_MUL = 0x55, + HID_KEY_KEYPAD_SUB = 0x56, + HID_KEY_KEYPAD_ADD = 0x57, + HID_KEY_KEYPAD_ENTER = 0x58, + HID_KEY_KEYPAD_1 = 0x59, + HID_KEY_KEYPAD_2 = 0x5A, + HID_KEY_KEYPAD_3 = 0x5B, + HID_KEY_KEYPAD_4 = 0x5C, + HID_KEY_KEYPAD_5 = 0x5D, + HID_KEY_KEYPAD_6 = 0x5E, + HID_KEY_KEYPAD_7 = 0x5F, + HID_KEY_KEYPAD_8 = 0x60, + HID_KEY_KEYPAD_9 = 0x61, + HID_KEY_KEYPAD_0 = 0x62, + HID_KEY_KEYPAD_DELETE = 0x63, + HID_KEY_KEYPAD_SLASH = 0x64, + HID_KEY_APPLICATION = 0x65, + HID_KEY_POWER = 0x66, + HID_KEY_KEYPAD_EQUAL = 0x67, + HID_KEY_F13 = 0x68, + HID_KEY_F14 = 0x69, + HID_KEY_F15 = 0x6A, + HID_KEY_F16 = 0x6B, + HID_KEY_F17 = 0x6C, + HID_KEY_F18 = 0x6D, + HID_KEY_F19 = 0x6E, + HID_KEY_F20 = 0x6F, + HID_KEY_F21 = 0x70, + HID_KEY_F22 = 0x71, + HID_KEY_F23 = 0x72, + HID_KEY_F24 = 0x73, + HID_KEY_EXECUTE = 0x74, + HID_KEY_HELP = 0x75, + HID_KEY_MENU = 0x76, + HID_KEY_SELECT = 0x77, + HID_KEY_STOP = 0x78, + HID_KEY_AGAIN = 0x79, + HID_KEY_UNDO = 0x7A, + HID_KEY_CUT = 0x7B, + HID_KEY_COPY = 0x7C, + HID_KEY_PASTE = 0x7D, + HID_KEY_FIND = 0x7E, + HID_KEY_MUTE = 0x7F, + HID_KEY_VOLUME_UP = 0x80, + HID_KEY_VOLUME_DOWN = 0x81, + HID_KEY_LOCKING_CAPS_LOCK = 0x82, + HID_KEY_LOCKING_NUM_LOCK = 0x83, + HID_KEY_LOCKING_SCROLL_LOCK = 0x84, + HID_KEY_KEYPAD_COMMA = 0x85, + HID_KEY_KEYPAD_EQUAL_SIGN = 0x86, + HID_KEY_INTERNATIONAL_1 = 0x87, + HID_KEY_INTERNATIONAL_2 = 0x88, + HID_KEY_INTERNATIONAL_3 = 0x89, + HID_KEY_INTERNATIONAL_4 = 0x8A, + HID_KEY_INTERNATIONAL_5 = 0x8B, + HID_KEY_INTERNATIONAL_6 = 0x8C, + HID_KEY_INTERNATIONAL_7 = 0x8D, + HID_KEY_INTERNATIONAL_8 = 0x8E, + HID_KEY_INTERNATIONAL_9 = 0x8F, + HID_KEY_LANG_1 = 0x90, + HID_KEY_LANG_2 = 0x91, + HID_KEY_LANG_3 = 0x92, + HID_KEY_LANG_4 = 0x93, + HID_KEY_LANG_5 = 0x94, + HID_KEY_LANG_6 = 0x95, + HID_KEY_LANG_7 = 0x96, + HID_KEY_LANG_8 = 0x97, + HID_KEY_LANG_9 = 0x98, + HID_KEY_ALTERNATE_ERASE = 0x99, + HID_KEY_SYSREQ = 0x9A, + HID_KEY_CANCEL = 0x9B, + HID_KEY_CLEAR = 0x9C, + HID_KEY_PRIOR = 0x9D, + HID_KEY_RETURN = 0x9E, + HID_KEY_SEPARATOR = 0x9F, + HID_KEY_OUT = 0xA0, + HID_KEY_OPER = 0xA1, + HID_KEY_CLEAR_AGAIN = 0xA2, + HID_KEY_CRSEL = 0xA3, + HID_KEY_EXSEL = 0xA4, + HID_KEY_KEYPAD_00 = 0xB0, + HID_KEY_KEYPAD_000 = 0xB1, + HID_KEY_THOUSANDS_SEPARATOR = 0xB2, + HID_KEY_DECIMAL_SEPARATOR = 0xB3, + HID_KEY_CURRENCY_UNIT = 0xB4, + HID_KEY_CURRENCY_SUB_UNIT = 0xB5, + HID_KEY_KEYPAD_OPEN_PARENTHESIS = 0xB6, + HID_KEY_KEYPAD_CLOSE_PARENTHESIS = 0xB7, + HID_KEY_KEYPAD_OPEN_BRACE = 0xB8, + HID_KEY_KEYPAD_CLOSE_BRACE = 0xB9, + HID_KEY_KEYPAD_TAB = 0xBA, + HID_KEY_KEYPAD_BACKSPACE = 0xBB, + HID_KEY_KEYPAD_A = 0xBC, + HID_KEY_KEYPAD_B = 0xBD, + HID_KEY_KEYPAD_C = 0xBE, + HID_KEY_KEYPAD_D = 0xBF, + HID_KEY_KEYPAD_E = 0xC0, + HID_KEY_KEYPAD_F = 0xC1, + HID_KEY_KEYPAD_XOR = 0xC2, + HID_KEY_KEYPAD_CARET = 0xC3, + HID_KEY_KEYPAD_PERCENT = 0xC4, + HID_KEY_KEYPAD_LESSER = 0xC5, + HID_KEY_KEYPAD_GREATER = 0xC6, + HID_KEY_KEYPAD_AND = 0xC7, + HID_KEY_KEYPAD_LOGICAL_AND = 0xC8, + HID_KEY_KEYPAD_OR = 0xC9, + HID_KEY_KEYPAD_LOGICAL_OR = 0xCA, + HID_KEY_KEYPAD_COLON = 0xCB, + HID_KEY_KEYPAD_SHARP = 0xCC, + HID_KEY_KEYPAD_SPACE = 0xCD, + HID_KEY_KEYPAD_AT = 0xCE, + HID_KEY_KEYPAD_BANG = 0xCF, + HID_KEY_KEYPAD_MEMORY_STORE = 0xD0, + HID_KEY_KEYPAD_MEMORY_RECALL = 0xD1, + HID_KEY_KEYPAD_MEMORY_CLEAD = 0xD2, + HID_KEY_KEYPAD_MEMORY_ADD = 0xD3, + HID_KEY_KEYPAD_MEMORY_SUBSTRACT = 0xD4, + HID_KEY_KEYPAD_MEMORY_MULTIPLY = 0xD5, + HID_KEY_KEYPAD_MEMORY_DIVIDE = 0xD6, + HID_KEY_KEYPAD_SIGN = 0xD7, + HID_KEY_KEYPAD_CLEAR = 0xD8, + HID_KEY_KEYPAD_CLEAR_ENTRY = 0xD9, + HID_KEY_KEYPAD_BINARY = 0xDA, + HID_KEY_KEYPAD_OCTAL = 0xDB, + HID_KEY_KEYPAD_DECIMAL = 0xDC, + HID_KEY_KEYPAD_HEXADECIMAL = 0xDD, + HID_KEY_LEFT_CONTROL = 0xE0, + HID_KEY_LEFT_SHIFT = 0xE1, + HID_KEY_LEFT_ALT = 0xE2, + HID_KEY_LEFT_GUI = 0xE3, + HID_KEY_RIGHT_CONTROL = 0xE0, + HID_KEY_RIGHT_SHIFT = 0xE1, + HID_KEY_RIGHT_ALT = 0xE2, + HID_KEY_RIGHT_GUI = 0xE3 +} __attribute__((packed)) hid_key_t; + +// Modifier bit mask +#define HID_LEFT_CONTROL (1 << 0) +#define HID_LEFT_SHIFT (1 << 1) +#define HID_LEFT_ALT (1 << 2) +#define HID_LEFT_GUI (1 << 3) +#define HID_RIGHT_CONTROL (1 << 4) +#define HID_RIGHT_SHIFT (1 << 5) +#define HID_RIGHT_ALT (1 << 6) +#define HID_RIGHT_GUI (1 << 7) + +/** + * @brief HID Keyboard Key number for Boot Interface + * + * @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef enum { + HID_KEYBOARD_KEY_NUMBER0 = 0, + HID_KEYBOARD_KEY_NUMBER1, + HID_KEYBOARD_KEY_NUMBER2, + HID_KEYBOARD_KEY_NUMBER3, + HID_KEYBOARD_KEY_NUMBER4, + HID_KEYBOARD_KEY_NUMBER5, + HID_KEYBOARD_KEY_MAX, +} hid_keyboard_key_number_t; + +/** + * @brief HID Keyboard Input Report for Boot Interfaces + * + * @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef struct { + union { + struct { + uint8_t left_ctr: 1; + uint8_t left_shift: 1; + uint8_t left_alt: 1; + uint8_t left_gui: 1; + uint8_t rigth_ctr: 1; + uint8_t right_shift: 1; + uint8_t right_alt: 1; + uint8_t right_gui: 1; + }; + uint8_t val; + } modifier; + uint8_t reserved; + uint8_t key[HID_KEYBOARD_KEY_MAX]; +} __attribute__((packed)) hid_keyboard_input_report_boot_t; + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/examples/USB_Host_Keyboard_Mouse/src/hid_usage_mouse.h b/examples/USB_Host_Keyboard_Mouse/src/hid_usage_mouse.h new file mode 100644 index 0000000..5ef0183 --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/src/hid_usage_mouse.h @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief HID Mouse Input Report for Boot Interfaces + * + * @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11 + */ +typedef struct { + union { + struct { + uint8_t button1: 1; + uint8_t button2: 1; + uint8_t button3: 1; + uint8_t reserved: 5; + }; + uint8_t val; + } buttons; + int8_t x_displacement; + int8_t y_displacement; +} __attribute__((packed)) hid_mouse_input_report_boot_t; + +#ifdef __cplusplus +} +#endif //__cplusplus diff --git a/examples/USB_Host_Keyboard_Mouse/usb_hid.cpp b/examples/USB_Host_Keyboard_Mouse/usb_hid.cpp new file mode 100644 index 0000000..104118a --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/usb_hid.cpp @@ -0,0 +1,607 @@ +/* + * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "freertos/queue.h" +#include "esp_err.h" +#include "esp_log.h" +#include "usb/usb_host.h" +#include "errno.h" +#include "driver/gpio.h" + +#include "src/hid_host.h" +#include "src/hid_usage_keyboard.h" +#include "src/hid_usage_mouse.h" +#include +#include "usb_hid.h" + +/* GPIO Pin number for quit from example logic */ +#define APP_QUIT_PIN GPIO_NUM_0 + +static const char *TAG = "example"; +static QueueHandle_t hid_host_event_queue; +static bool user_shutdown = false; +static QueueHandle_t queue_in = NULL; +static struct InputData pdat = {0}; + +/** + * @brief HID Host event + * + * This event is used for delivering the HID Host event from callback to a task. + */ +typedef struct { + hid_host_device_handle_t hid_device_handle; + hid_host_driver_event_t event; + void *arg; +} hid_host_event_queue_t; + +/** + * @brief HID Protocol string names + */ +static const char *hid_proto_name_str[] = { + "NONE", + "KEYBOARD", + "MOUSE" +}; + +/** + * @brief Key event + */ +enum key_state { + KEY_STATE_PRESSED = 0x00, + KEY_STATE_RELEASED = 0x01 +} ; + +typedef struct { + key_state state; + uint8_t modifier; + uint8_t key_code; +} key_event_t; + +/* Main char symbol for ENTER key */ +#define KEYBOARD_ENTER_MAIN_CHAR '\r' +/* When set to 1 pressing ENTER will be extending with LineFeed during serial debug output */ +#define KEYBOARD_ENTER_LF_EXTEND 1 + +/** + * @brief Scancode to ascii table + */ +const uint8_t keycode2ascii [57][2] = { + {0, 0}, /* HID_KEY_NO_PRESS */ + {0, 0}, /* HID_KEY_ROLLOVER */ + {0, 0}, /* HID_KEY_POST_FAIL */ + {0, 0}, /* HID_KEY_ERROR_UNDEFINED */ + {'a', 'A'}, /* HID_KEY_A */ + {'b', 'B'}, /* HID_KEY_B */ + {'c', 'C'}, /* HID_KEY_C */ + {'d', 'D'}, /* HID_KEY_D */ + {'e', 'E'}, /* HID_KEY_E */ + {'f', 'F'}, /* HID_KEY_F */ + {'g', 'G'}, /* HID_KEY_G */ + {'h', 'H'}, /* HID_KEY_H */ + {'i', 'I'}, /* HID_KEY_I */ + {'j', 'J'}, /* HID_KEY_J */ + {'k', 'K'}, /* HID_KEY_K */ + {'l', 'L'}, /* HID_KEY_L */ + {'m', 'M'}, /* HID_KEY_M */ + {'n', 'N'}, /* HID_KEY_N */ + {'o', 'O'}, /* HID_KEY_O */ + {'p', 'P'}, /* HID_KEY_P */ + {'q', 'Q'}, /* HID_KEY_Q */ + {'r', 'R'}, /* HID_KEY_R */ + {'s', 'S'}, /* HID_KEY_S */ + {'t', 'T'}, /* HID_KEY_T */ + {'u', 'U'}, /* HID_KEY_U */ + {'v', 'V'}, /* HID_KEY_V */ + {'w', 'W'}, /* HID_KEY_W */ + {'x', 'X'}, /* HID_KEY_X */ + {'y', 'Y'}, /* HID_KEY_Y */ + {'z', 'Z'}, /* HID_KEY_Z */ + {'1', '!'}, /* HID_KEY_1 */ + {'2', '@'}, /* HID_KEY_2 */ + {'3', '#'}, /* HID_KEY_3 */ + {'4', '$'}, /* HID_KEY_4 */ + {'5', '%'}, /* HID_KEY_5 */ + {'6', '^'}, /* HID_KEY_6 */ + {'7', '&'}, /* HID_KEY_7 */ + {'8', '*'}, /* HID_KEY_8 */ + {'9', '('}, /* HID_KEY_9 */ + {'0', ')'}, /* HID_KEY_0 */ + {KEYBOARD_ENTER_MAIN_CHAR, KEYBOARD_ENTER_MAIN_CHAR}, /* HID_KEY_ENTER */ + {0, 0}, /* HID_KEY_ESC */ + {'\b', 0}, /* HID_KEY_DEL */ + {0, 0}, /* HID_KEY_TAB */ + {' ', ' '}, /* HID_KEY_SPACE */ + {'-', '_'}, /* HID_KEY_MINUS */ + {'=', '+'}, /* HID_KEY_EQUAL */ + {'[', '{'}, /* HID_KEY_OPEN_BRACKET */ + {']', '}'}, /* HID_KEY_CLOSE_BRACKET */ + {'\\', '|'}, /* HID_KEY_BACK_SLASH */ + {'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH + {';', ':'}, /* HID_KEY_COLON */ + {'\'', '"'}, /* HID_KEY_QUOTE */ + {'`', '~'}, /* HID_KEY_TILDE */ + {',', '<'}, /* HID_KEY_LESS */ + {'.', '>'}, /* HID_KEY_GREATER */ + {'/', '?'} /* HID_KEY_SLASH */ +}; + +/** + * @brief Makes new line depending on report output protocol type + * + * @param[in] proto Current protocol to output + */ +static void hid_print_new_device_report_header(hid_protocol_t proto) +{ + static hid_protocol_t prev_proto_output = HID_PROTOCOL_MAX; + + if (prev_proto_output != proto) { + prev_proto_output = proto; + Serial.printf("\r\n"); + if (proto == HID_PROTOCOL_MOUSE) { + Serial.printf("Mouse\r\n"); + } else if (proto == HID_PROTOCOL_KEYBOARD) { + Serial.printf("Keyboard\r\n"); + } else { + Serial.printf("Generic\r\n"); + } + fflush(stdout); + } +} + +/** + * @brief HID Keyboard modifier verification for capitalization application (right or left shift) + * + * @param[in] modifier + * @return true Modifier was pressed (left or right shift) + * @return false Modifier was not pressed (left or right shift) + * + */ +static inline bool hid_keyboard_is_modifier_shift(uint8_t modifier) +{ + if (((modifier & HID_LEFT_SHIFT) == HID_LEFT_SHIFT) || + ((modifier & HID_RIGHT_SHIFT) == HID_RIGHT_SHIFT)) { + return true; + } + return false; +} + +/** + * @brief HID Keyboard get char symbol from key code + * + * @param[in] modifier Keyboard modifier data + * @param[in] key_code Keyboard key code + * @param[in] key_char Pointer to key char data + * + * @return true Key scancode converted successfully + * @return false Key scancode unknown + */ +static inline bool hid_keyboard_get_char(uint8_t modifier, + uint8_t key_code, + unsigned char *key_char) +{ + uint8_t mod = (hid_keyboard_is_modifier_shift(modifier)) ? 1 : 0; + + if ((key_code >= HID_KEY_A) && (key_code <= HID_KEY_SLASH)) { + *key_char = keycode2ascii[key_code][mod]; + } else { + // All other key pressed + return false; + } + + return true; +} + +/** + * @brief HID Keyboard print char symbol + * + * @param[in] key_char Keyboard char to stdout + */ +static inline void hid_keyboard_print_char(unsigned int key_char) +{ + if (!!key_char) { + if (queue_in) { + pdat.id = 'k'; + pdat.key = key_char; + xQueueSend(queue_in, &pdat, portMAX_DELAY); + } + putchar(key_char); +#if (KEYBOARD_ENTER_LF_EXTEND) + if (KEYBOARD_ENTER_MAIN_CHAR == key_char) { + putchar('\n'); + if (queue_in) { + pdat.id = 'k'; + pdat.key = '\n'; + xQueueSend(queue_in, &pdat, portMAX_DELAY); + } + } +#endif // KEYBOARD_ENTER_LF_EXTEND + fflush(stdout); + } +} + +/** + * @brief Key Event. Key event with the key code, state and modifier. + * + * @param[in] key_event Pointer to Key Event structure + * + */ +static void key_event_callback(key_event_t *key_event) +{ + unsigned char key_char; + + hid_print_new_device_report_header(HID_PROTOCOL_KEYBOARD); + + if (KEY_STATE_PRESSED == key_event->state) { + if (hid_keyboard_get_char(key_event->modifier, + key_event->key_code, &key_char)) { + + hid_keyboard_print_char(key_char); + + } + } +} + +/** + * @brief Key buffer scan code search. + * + * @param[in] src Pointer to source buffer where to search + * @param[in] key Key scancode to search + * @param[in] length Size of the source buffer + */ +static inline bool key_found(const uint8_t *const src, + uint8_t key, + unsigned int length) +{ + for (unsigned int i = 0; i < length; i++) { + if (src[i] == key) { + return true; + } + } + return false; +} + +/** + * @brief USB HID Host Keyboard Interface report callback handler + * + * @param[in] data Pointer to input report data buffer + * @param[in] length Length of input report data buffer + */ +static void hid_host_keyboard_report_callback(const uint8_t *const data, const int length) +{ + hid_keyboard_input_report_boot_t *kb_report = (hid_keyboard_input_report_boot_t *)data; + + if (length < sizeof(hid_keyboard_input_report_boot_t)) { + return; + } + + static uint8_t prev_keys[HID_KEYBOARD_KEY_MAX] = { 0 }; + key_event_t key_event; + + for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) { + + // key has been released verification + if (prev_keys[i] > HID_KEY_ERROR_UNDEFINED && + !key_found(kb_report->key, prev_keys[i], HID_KEYBOARD_KEY_MAX)) { + key_event.key_code = prev_keys[i]; + key_event.modifier = 0; + key_event.state = KEY_STATE_RELEASED; + key_event_callback(&key_event); + } + + // key has been pressed verification + if (kb_report->key[i] > HID_KEY_ERROR_UNDEFINED && + !key_found(prev_keys, kb_report->key[i], HID_KEYBOARD_KEY_MAX)) { + key_event.key_code = kb_report->key[i]; + key_event.modifier = kb_report->modifier.val; + key_event.state = KEY_STATE_PRESSED; + key_event_callback(&key_event); + } + } + + memcpy(prev_keys, &kb_report->key, HID_KEYBOARD_KEY_MAX); +} + + +/** + * @brief USB HID Host Mouse Interface report callback handler + * + * @param[in] data Pointer to input report data buffer + * @param[in] length Length of input report data buffer + */ +static void hid_host_mouse_report_callback(const uint8_t *const data, const int length) +{ + hid_mouse_input_report_boot_t *mouse_report = (hid_mouse_input_report_boot_t *)data; + + if (length < sizeof(hid_mouse_input_report_boot_t)) { + return; + } + + static int x_pos = 0; + static int y_pos = 0; + + // Calculate absolute position from displacement + x_pos += mouse_report->x_displacement; + y_pos += mouse_report->y_displacement; + + hid_print_new_device_report_header(HID_PROTOCOL_MOUSE); + + + if (queue_in) { + pdat.id = 'm'; + pdat.left = mouse_report->buttons.button1; + pdat.right = mouse_report->buttons.button2; + pdat.x = x_pos; + pdat.y = y_pos; + // Serial.printf("X: %06d\tY: %06d\t|%c|%c|\r", + // x_pos, y_pos, + // (mouse_report->buttons.button1 ? 'o' : ' '), + // (mouse_report->buttons.button2 ? 'o' : ' ')); + // Serial.println(); + + xQueueSend(queue_in, &pdat, portMAX_DELAY); + } + +} + +/** + * @brief USB HID Host Generic Interface report callback handler + * + * 'generic' means anything else than mouse or keyboard + * + * @param[in] data Pointer to input report data buffer + * @param[in] length Length of input report data buffer + */ +static void hid_host_generic_report_callback(const uint8_t *const data, const int length) +{ + hid_print_new_device_report_header(HID_PROTOCOL_NONE); + for (int i = 0; i < length; i++) { + Serial.printf("%02X", data[i]); + } + putchar('\r'); +} + + +/** + * @brief USB HID Host interface callback + * + * @param[in] hid_device_handle HID Device handle + * @param[in] event HID Host interface event + * @param[in] arg Pointer to arguments, does not used + */ +void hid_host_interface_callback(hid_host_device_handle_t hid_device_handle, + const hid_host_interface_event_t event, + void *arg) +{ + uint8_t data[64] = { 0 }; + size_t data_length = 0; + hid_host_dev_params_t dev_params; + ESP_ERROR_CHECK( hid_host_device_get_params(hid_device_handle, &dev_params)); + + switch (event) { + case HID_HOST_INTERFACE_EVENT_INPUT_REPORT: + ESP_ERROR_CHECK( hid_host_device_get_raw_input_report_data(hid_device_handle, + data, + 64, + &data_length)); + + if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) { + if (HID_PROTOCOL_KEYBOARD == dev_params.proto) { + hid_host_keyboard_report_callback(data, data_length); + } else if (HID_PROTOCOL_MOUSE == dev_params.proto) { + hid_host_mouse_report_callback(data, data_length); + } + } else { + hid_host_generic_report_callback(data, data_length); + } + + break; + case HID_HOST_INTERFACE_EVENT_DISCONNECTED: + Serial.printf("HID Device, protocol '%s' DISCONNECTED", + hid_proto_name_str[dev_params.proto]); + ESP_ERROR_CHECK( hid_host_device_close(hid_device_handle) ); + break; + case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR: + Serial.printf("HID Device, protocol '%s' TRANSFER_ERROR", + hid_proto_name_str[dev_params.proto]); + break; + default: + ESP_LOGE(TAG, "HID Device, protocol '%s' Unhandled event", + hid_proto_name_str[dev_params.proto]); + break; + } +} + +/** + * @brief USB HID Host Device event + * + * @param[in] hid_device_handle HID Device handle + * @param[in] event HID Host Device event + * @param[in] arg Pointer to arguments, does not used + */ +void hid_host_device_event(hid_host_device_handle_t hid_device_handle, + const hid_host_driver_event_t event, + void *arg) +{ + hid_host_dev_params_t dev_params; + ESP_ERROR_CHECK( hid_host_device_get_params(hid_device_handle, &dev_params)); + + switch (event) { + case HID_HOST_DRIVER_EVENT_CONNECTED: { + Serial.printf("HID Device, protocol '%s' CONNECTED", + hid_proto_name_str[dev_params.proto]); + + const hid_host_device_config_t dev_config = { + .callback = hid_host_interface_callback, + .callback_arg = NULL + }; + + ESP_ERROR_CHECK( hid_host_device_open(hid_device_handle, &dev_config) ); + if (HID_SUBCLASS_BOOT_INTERFACE == dev_params.sub_class) { + ESP_ERROR_CHECK( hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT)); + if (HID_PROTOCOL_KEYBOARD == dev_params.proto) { + ESP_ERROR_CHECK( hid_class_request_set_idle(hid_device_handle, 0, 0)); + } + } + ESP_ERROR_CHECK( hid_host_device_start(hid_device_handle) ); + } + break; + default: + break; + } +} + +/** + * @brief Start USB Host install and handle common USB host library events while app pin not low + * + * @param[in] arg Not used + */ +static void usb_lib_task(void *arg) +{ + const gpio_config_t input_pin = { + .pin_bit_mask = BIT64(APP_QUIT_PIN), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + }; + ESP_ERROR_CHECK( gpio_config(&input_pin) ); + + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + + ESP_ERROR_CHECK( usb_host_install(&host_config) ); + xTaskNotifyGive(arg); + + while (gpio_get_level(APP_QUIT_PIN) != 0) { + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + + // Release devices once all clients has deregistered + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + usb_host_device_free_all(); + Serial.println("USB Event flags: NO_CLIENTS"); + } + // All devices were removed + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + Serial.println("USB Event flags: ALL_FREE"); + } + } + // App Button was pressed, trigger the flag + user_shutdown = true; + Serial.println("USB shutdown"); + // Clean up USB Host + vTaskDelay(10); // Short delay to allow clients clean-up + ESP_ERROR_CHECK( usb_host_uninstall()); + vTaskDelete(NULL); +} + +/** + * @brief HID Host main task + * + * Creates queue and get new event from the queue + * + * @param[in] pvParameters Not used + */ +void hid_host_task(void *pvParameters) +{ + hid_host_event_queue_t evt_queue; + // Create queue + hid_host_event_queue = xQueueCreate(10, sizeof(hid_host_event_queue_t)); + + // Wait queue + while (!user_shutdown) { + if (xQueueReceive(hid_host_event_queue, &evt_queue, pdMS_TO_TICKS(50))) { + hid_host_device_event(evt_queue.hid_device_handle, + evt_queue.event, + evt_queue.arg); + } + } + + xQueueReset(hid_host_event_queue); + vQueueDelete(hid_host_event_queue); + vTaskDelete(NULL); +} + +/** + * @brief HID Host Device callback + * + * Puts new HID Device event to the queue + * + * @param[in] hid_device_handle HID Device handle + * @param[in] event HID Device event + * @param[in] arg Not used + */ +void hid_host_device_callback(hid_host_device_handle_t hid_device_handle, + const hid_host_driver_event_t event, + void *arg) +{ + const hid_host_event_queue_t evt_queue = { + .hid_device_handle = hid_device_handle, + .event = event, + .arg = arg + }; + xQueueSend(hid_host_event_queue, &evt_queue, 0); +} + +void setupUSB(QueueHandle_t queue_i) +{ + + queue_in = queue_i; + + BaseType_t task_created; + Serial.println("HID Host example"); + + /* + * Create usb_lib_task to: + * - initialize USB Host library + * - Handle USB Host events while APP pin in in HIGH state + */ + task_created = xTaskCreatePinnedToCore(usb_lib_task, + "usb_events", + 4096, + xTaskGetCurrentTaskHandle(), + 2, NULL, 0); + assert(task_created == pdTRUE); + + // Wait for notification from usb_lib_task to proceed + ulTaskNotifyTake(false, 1000); + + /* + * HID host driver configuration + * - create background task for handling low level event inside the HID driver + * - provide the device callback to get new HID Device connection event + */ + const hid_host_driver_config_t hid_host_driver_config = { + .create_background_task = true, + .task_priority = 5, + .stack_size = 4096, + .core_id = 0, + .callback = hid_host_device_callback, + .callback_arg = NULL + }; + + ESP_ERROR_CHECK( hid_host_install(&hid_host_driver_config) ); + + // Task is working until the devices are gone (while 'user_shutdown' if false) + user_shutdown = false; + + /* + * Create HID Host task process for handle events + * IMPORTANT: Task is necessary here while there is no possibility to interact + * with USB device from the callback. + */ + task_created = xTaskCreate(&hid_host_task, "hid_task", 4 * 1024, NULL, 2, NULL); + assert(task_created == pdTRUE); +} + + diff --git a/examples/USB_Host_Keyboard_Mouse/usb_hid.h b/examples/USB_Host_Keyboard_Mouse/usb_hid.h new file mode 100644 index 0000000..6482282 --- /dev/null +++ b/examples/USB_Host_Keyboard_Mouse/usb_hid.h @@ -0,0 +1,18 @@ +/** + * @file usb_hid.h + * @author Lewis He (lewishe@outlook.com) + * @license MIT + * @copyright Copyright (c) 2024 Shenzhen Xin Yuan Electronic Technology Co., Ltd + * @date 2024-11-26 + * + */ + +#pragma once + + +#include "InputParams.h" + + +void setupUSB(QueueHandle_t queue_i); + + diff --git a/platformio.ini b/platformio.ini index 7583e82..3694180 100644 --- a/platformio.ini +++ b/platformio.ini @@ -34,7 +34,7 @@ default_envs = T-Display-AMOLED ; src_dir = examples/Factory ; src_dir = examples/Touchpad ; src_dir = examples/Lvgl_Images -src_dir = examples/LVGL_SD_Images +; src_dir = examples/LVGL_SD_Images ; src_dir = examples/TFT_eSPI_Sprite ; src_dir = examples/TFT_eSPI_Sprite_ArcFill ; src_dir = examples/TFT_eSPI_Sprite_RLE_Font @@ -110,7 +110,7 @@ src_dir = examples/LVGL_SD_Images ; src_dir = examples/lvgl/third_party/gif ; src_dir = examples/lvgl/third_party/ImageDecoder - +src_dir = examples/USB_Host_Keyboard_Mouse ;! Don't make changes boards_dir = boards @@ -128,10 +128,11 @@ build_flags = -DDISABLE_ALL_LIBRARY_WARNINGS ; Enable -DARDUINO_USB_CDC_ON_BOOT will start printing and wait for terminal access during startup - -DARDUINO_USB_CDC_ON_BOOT=1 + ; -DARDUINO_USB_CDC_ON_BOOT=1 + ; USB host example must disable usb cdc ; Enable -UARDUINO_USB_CDC_ON_BOOT will turn off printing and will not block when using the battery - ; -UARDUINO_USB_CDC_ON_BOOT + -UARDUINO_USB_CDC_ON_BOOT -DCORE_DEBUG_LEVEL=1 monitor_filters =