Files
CherryUSB/platform/lvgl/usbh_hid_lvgl.c
2025-05-07 09:21:31 +08:00

455 lines
16 KiB
C

/*
* Copyright (c) 2025, sakumisu
* Copyright (c) 2025, RCSN
*
* SPDX-License-Identifier: Apache-2.0
*/
//Refer to https://github.com/espressif/esp-bsp/blob/master/components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_usbhid.c
#include "usbh_core.h"
#include "usbh_hid.h"
#include "usbh_hid_lvgl.h"
/* LVGL image of cursor */
LV_IMG_DECLARE(img_cursor)
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 */
{'\r', '\r'}, /* 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 */
{';', ':'}, /* HID_KEY_COLON */
{'\'', '"'}, /* HID_KEY_QUOTE */
{'`', '~'}, /* HID_KEY_TILDE */
{',', '<'}, /* HID_KEY_LESS */
{'.', '>'}, /* HID_KEY_GREATER */
{'/', '?'} /* HID_KEY_SLASH */
};
struct usbh_hid_lvgl {
struct {
lv_indev_t *indev; /* LVGL mouse input device driver */
uint8_t sensitivity; /* Mouse sensitivity (cannot be zero) */
int16_t x; /* Mouse X coordinate */
int16_t y; /* Mouse Y coordinate */
bool left_button; /* Mouse left button state */
} mouse;
struct {
lv_indev_t *indev; /* LVGL keyboard input device driver */
uint32_t last_key;
bool pressed;
} kb;
};
static struct usbh_hid_lvgl g_hid_lvgl;
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t hid_mouse_buffer[64];
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t hid_keyboard_buffer[64];
#if defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 9)
static void usbh_hid_lvgl_read_mouse(lv_indev_t *indev_drv, lv_indev_data_t *data)
{
int16_t width = 0;
int16_t height = 0;
struct usbh_hid_lvgl *ctx = &g_hid_lvgl;
lv_display_t *disp = lv_indev_get_display(indev_drv);
if (lv_display_get_rotation(disp) == LV_DISPLAY_ROTATION_0 || lv_display_get_rotation(disp) == LV_DISPLAY_ROTATION_180) {
width = lv_display_get_physical_horizontal_resolution(disp);
height = lv_display_get_vertical_resolution(disp);
} else {
width = lv_display_get_vertical_resolution(disp);
height = lv_display_get_physical_horizontal_resolution(disp);
}
/* Screen borders */
if (ctx->mouse.x < 0) {
ctx->mouse.x = 0;
} else if (ctx->mouse.x >= width * ctx->mouse.sensitivity) {
ctx->mouse.x = (width * ctx->mouse.sensitivity) - 1;
}
if (ctx->mouse.y < 0) {
ctx->mouse.y = 0;
} else if (ctx->mouse.y >= height * ctx->mouse.sensitivity) {
ctx->mouse.y = (height * ctx->mouse.sensitivity) - 1;
}
/* Get coordinates by rotation with sensitivity */
switch (lv_display_get_rotation(disp)) {
case LV_DISPLAY_ROTATION_0:
data->point.x = ctx->mouse.x / ctx->mouse.sensitivity;
data->point.y = ctx->mouse.y / ctx->mouse.sensitivity;
break;
case LV_DISPLAY_ROTATION_90:
data->point.y = width - ctx->mouse.x / ctx->mouse.sensitivity;
data->point.x = ctx->mouse.y / ctx->mouse.sensitivity;
break;
case LV_DISPLAY_ROTATION_180:
data->point.x = width - ctx->mouse.x / ctx->mouse.sensitivity;
data->point.y = height - ctx->mouse.y / ctx->mouse.sensitivity;
break;
case LV_DISPLAY_ROTATION_270:
data->point.y = ctx->mouse.x / ctx->mouse.sensitivity;
data->point.x = height - ctx->mouse.y / ctx->mouse.sensitivity;
break;
}
if (ctx->mouse.left_button) {
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
static void usbh_hid_lvgl_read_keyboard(lv_indev_t *indev_drv, lv_indev_data_t *data)
{
(void)indev_drv;
struct usbh_hid_lvgl *ctx = &g_hid_lvgl;
data->key = ctx->kb.last_key;
if (ctx->kb.pressed) {
data->state = LV_INDEV_STATE_PRESSED;
ctx->kb.pressed = false;
} else {
data->state = LV_INDEV_STATE_RELEASED;
ctx->kb.last_key = 0;
}
}
#elif defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 8)
static lv_indev_drv_t indev_drv;
static lv_indev_drv_t keyboard_drv;
static void hpm_lvgl_indev_read_cb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
int16_t width = 0;
int16_t height = 0;
struct usbh_hid_lvgl *ctx = &g_hid_lvgl;
if ((indev_drv->disp->driver->rotated == LV_DISP_ROT_NONE) || (indev_drv->disp->driver->rotated == LV_DISP_ROT_180)) {
width = indev_drv->disp->driver->hor_res;
height = indev_drv->disp->driver->ver_res;
} else {
width = indev_drv->disp->driver->ver_res;
height = indev_drv->disp->driver->hor_res;
}
/* Screen borders */
if (ctx->mouse.x < 0) {
ctx->mouse.x = 0;
} else if (ctx->mouse.x >= width * ctx->mouse.sensitivity) {
ctx->mouse.x = (width * ctx->mouse.sensitivity) - 1;
}
if (ctx->mouse.y < 0) {
ctx->mouse.y = 0;
} else if (ctx->mouse.y >= height * ctx->mouse.sensitivity) {
ctx->mouse.y = (height * ctx->mouse.sensitivity) - 1;
}
/* Get coordinates by rotation with sensitivity */
switch (indev_drv->disp->driver->rotated) {
case LV_DISP_ROT_NONE:
data->point.x = ctx->mouse.x / ctx->mouse.sensitivity;
data->point.y = ctx->mouse.y / ctx->mouse.sensitivity;
break;
case LV_DISP_ROT_90:
data->point.y = width - ctx->mouse.x / ctx->mouse.sensitivity;
data->point.x = ctx->mouse.y / ctx->mouse.sensitivity;
break;
case LV_DISP_ROT_180:
data->point.x = width - ctx->mouse.x / ctx->mouse.sensitivity;
data->point.y = height - ctx->mouse.y / ctx->mouse.sensitivity;
break;
case LV_DISP_ROT_270:
data->point.y = ctx->mouse.x / ctx->mouse.sensitivity;
data->point.x = height - ctx->mouse.y / ctx->mouse.sensitivity;
break;
}
if (ctx->mouse.left_button) {
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
}
}
static void usbh_hid_lvgl_read_keyboard(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
(void)indev_drv;
struct usbh_hid_lvgl *ctx = &g_hid_lvgl;
data->key = ctx->kb.last_key;
if (ctx->kb.pressed) {
data->state = LV_INDEV_STATE_PRESSED;
ctx->kb.pressed = false;
} else {
data->state = LV_INDEV_STATE_RELEASED;
ctx->kb.last_key = 0;
}
}
#endif
lv_indev_t *usbh_hid_lvgl_add_mouse(uint8_t sensitivity)
{
int32_t ver_res, hor_res;
lv_indev_t *indev;
/* Initialize USB HID */
struct usbh_hid_lvgl *hid_ctx = &g_hid_lvgl;
/* Mouse sensitivity cannot be zero */
hid_ctx->mouse.sensitivity = (sensitivity == 0 ? 1 : sensitivity);
#if defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 9)
ver_res = lv_display_get_vertical_resolution(lv_display_get_default());
hor_res = lv_display_get_physical_horizontal_resolution(lv_display_get_default());
#elif defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 8)
lv_disp_t * disp = lv_disp_get_default();
ver_res = disp->driver->hor_res;
hor_res = disp->driver->ver_res;
#endif
/* Default coordinates to screen center */
hid_ctx->mouse.x = (hor_res * hid_ctx->mouse.sensitivity) / 2;
hid_ctx->mouse.y = (ver_res * hid_ctx->mouse.sensitivity) / 2;
/* Register a mouse input device */
#if defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 9)
indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, usbh_hid_lvgl_read_mouse);
lv_indev_set_driver_data(indev, hid_ctx);
hid_ctx->mouse.indev = indev;
#elif defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 8)
lv_indev_drv_init(&indev_drv);
indev_drv.read_cb = hpm_lvgl_indev_read_cb;
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.user_data = (void *)hid_ctx;
indev = lv_indev_drv_register(&indev_drv);
#endif
/* Set image of cursor */
lv_obj_t *cursor;
cursor = lv_img_create(lv_scr_act());
lv_img_set_src(cursor, &img_cursor);
lv_indev_set_cursor(indev, cursor);
return indev;
}
lv_indev_t *usbh_hid_lvgl_add_keyboard(void)
{
lv_indev_t *indev;
/* Initialize USB HID */
struct usbh_hid_lvgl *hid_ctx = &g_hid_lvgl;
/* Register a keyboard input device */
#if defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 9)
indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(indev, usbh_hid_lvgl_read_keyboard);
lv_indev_set_driver_data(indev, hid_ctx);
hid_ctx->kb.indev = indev;
#elif defined(LVGL_VERSION_MAJOR) && (LVGL_VERSION_MAJOR == 8)
lv_indev_drv_init(&keyboard_drv);
keyboard_drv.read_cb = usbh_hid_lvgl_read_keyboard;
keyboard_drv.type = LV_INDEV_TYPE_KEYPAD;
keyboard_drv.user_data = (void *)hid_ctx;
indev = lv_indev_drv_register(&keyboard_drv);
#endif
return indev;
}
void usbh_hid_mouse_callback(void *arg, int nbytes)
{
struct usbh_hid *hid_class = (struct usbh_hid *)arg;
if (nbytes > 0) {
struct usbh_hid_lvgl *hid_ctx = &g_hid_lvgl;
hid_ctx->mouse.left_button = hid_mouse_buffer[0];
hid_ctx->mouse.x += (int8_t)hid_mouse_buffer[1];
hid_ctx->mouse.y += (int8_t)hid_mouse_buffer[2];
usbh_int_urb_fill(&hid_class->intin_urb, hid_class->hport, hid_class->intin, hid_mouse_buffer, hid_class->intin->wMaxPacketSize, 0,
usbh_hid_mouse_callback, hid_class);
usbh_submit_urb(&hid_class->intin_urb);
} else if (nbytes == -USB_ERR_NAK) { /* only dwc2 should do this */
usbh_int_urb_fill(&hid_class->intin_urb, hid_class->hport, hid_class->intin, hid_mouse_buffer, hid_class->intin->wMaxPacketSize, 0,
usbh_hid_mouse_callback, hid_class);
usbh_submit_urb(&hid_class->intin_urb);
} else {
}
}
static char usb_hid_get_keyboard_char(uint8_t key, uint8_t shift)
{
char ret_key = 0;
static bool cap_lock = false;
if (shift > 1) {
shift = 1;
}
if ((key >= HID_KBD_USAGE_A) && (key <= HID_KBD_USAGE_QUESTION)) {
ret_key = (cap_lock == false) ? keycode2ascii[key][0] : keycode2ascii[key][1];
} else if ((key >= HID_KBD_USAGE_1) && (key <= HID_KBD_USAGE_QUESTION)) {
ret_key = keycode2ascii[key][shift];
} else if ((key >= HID_KBD_USAGE_KPD1) && (key <= HID_KBD_USAGE_KPD0)) {
ret_key = keycode2ascii[(key - HID_KBD_USAGE_KPD1) + HID_KBD_USAGE_1][0];
} else if ((key >= HID_KBD_USAGE_KPDDIV) && (key <= HID_KBD_USAGE_KPDEMTER)) {
switch (key) {
case HID_KBD_USAGE_KPDDIV:
ret_key = '/';
break;
case HID_KBD_USAGE_KPDMUL:
ret_key = '*';
break;
case HID_KBD_USAGE_KPDHMINUS:
ret_key = '-';
break;
case HID_KBD_USAGE_KPDPLUS:
ret_key = '+';
break;
default:
break;
}
} else if (key == HID_KBD_USAGE_KPDDELETE) {
ret_key = '.';
} else if (key == HID_KBD_USAGE_CAPSLOCK) {
cap_lock = !cap_lock;
}
return ret_key;
}
void usbh_hid_keyboard_callback(void *arg, int nbytes)
{
struct usbh_hid *hid_class = (struct usbh_hid *)arg;
if (nbytes > 0) {
struct usbh_hid_lvgl *hid_ctx = &g_hid_lvgl;
struct usb_hid_kbd_report *keyboard = (struct usb_hid_kbd_report *)hid_keyboard_buffer;
for (int i = 0; i < 6; i++) {
if ((keyboard->key[i] <= HID_KBD_USAGE_MAX) && (keyboard->key[i] > HID_KBD_USAGE_NONE)) {
char key = 0;
/* LVGL special keys */
if (keyboard->key[i] == HID_KBD_USAGE_TAB) {
if (((keyboard->modifier & HID_MODIFIER_LSHIFT) || (keyboard->modifier & HID_MODIFIER_RSHIFT))) {
key = LV_KEY_PREV;
} else {
key = LV_KEY_NEXT;
}
} else if (keyboard->key[i] == HID_KBD_USAGE_RIGHT) {
key = LV_KEY_RIGHT;
} else if (keyboard->key[i] == HID_KBD_USAGE_LEFT) {
key = LV_KEY_LEFT;
} else if (keyboard->key[i] == HID_KBD_USAGE_DOWN) {
key = LV_KEY_DOWN;
} else if (keyboard->key[i] == HID_KBD_USAGE_UP) {
key = LV_KEY_UP;
} else if (keyboard->key[i] == HID_KBD_USAGE_ENTER || keyboard->key[i] == HID_KBD_USAGE_KPDEMTER) {
key = LV_KEY_ENTER;
} else if (keyboard->key[i] == HID_KBD_USAGE_DELETE) {
key = LV_KEY_BACKSPACE;
} else if (keyboard->key[i] == HID_KBD_USAGE_HOME) {
key = LV_KEY_HOME;
} else if (keyboard->key[i] == HID_KBD_USAGE_END) {
key = LV_KEY_END;
} else if (keyboard->key[i] == HID_KBD_USAGE_DELFWD) {
key = LV_KEY_DEL;
} else {
/* Get ASCII char */
key = usb_hid_get_keyboard_char(keyboard->key[i], ((keyboard->modifier & HID_MODIFIER_LSHIFT) || (keyboard->modifier & HID_MODIFIER_RSHIFT)));
}
if (key == 0) {
USB_LOG_ERR("Not recognized key: %c (%d)\r\n", keyboard->key[i], keyboard->key[i]);
}
hid_ctx->kb.last_key = key;
hid_ctx->kb.pressed = true;
}
}
usbh_int_urb_fill(&hid_class->intin_urb, hid_class->hport, hid_class->intin, hid_keyboard_buffer, hid_class->intin->wMaxPacketSize, 0,
usbh_hid_keyboard_callback, hid_class);
usbh_submit_urb(&hid_class->intin_urb);
} else if (nbytes == -USB_ERR_NAK) { /* only dwc2 should do this */
usbh_int_urb_fill(&hid_class->intin_urb, hid_class->hport, hid_class->intin, hid_keyboard_buffer, hid_class->intin->wMaxPacketSize, 0,
usbh_hid_keyboard_callback, hid_class);
usbh_submit_urb(&hid_class->intin_urb);
} else {
}
}
extern int usbh_hid_set_protocol(struct usbh_hid *hid_class, uint8_t protocol);
void usbh_hid_run(struct usbh_hid *hid_class)
{
usbh_hid_set_protocol(hid_class, 0);
if (hid_class->hport->config.intf[hid_class->intf].altsetting[0].intf_desc.bInterfaceProtocol == HID_PROTOCOL_KEYBOARD) {
usbh_int_urb_fill(&hid_class->intin_urb, hid_class->hport, hid_class->intin, hid_keyboard_buffer, hid_class->intin->wMaxPacketSize, 0,
usbh_hid_keyboard_callback, hid_class);
usbh_submit_urb(&hid_class->intin_urb);
} else if (hid_class->hport->config.intf[hid_class->intf].altsetting[0].intf_desc.bInterfaceProtocol == HID_PROTOCOL_MOUSE) {
usbh_int_urb_fill(&hid_class->intin_urb, hid_class->hport, hid_class->intin, hid_mouse_buffer, hid_class->intin->wMaxPacketSize, 0,
usbh_hid_mouse_callback, hid_class);
usbh_submit_urb(&hid_class->intin_urb);
} else {
}
}
void usbh_hid_stop(struct usbh_hid *hid_class)
{
(void)hid_class;
}