/* * Copyright (c) 2024 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(main, LOG_LEVEL_INF); static const uint8_t hid_report_desc[] = HID_KEYBOARD_REPORT_DESC(); enum kb_leds_idx { KB_LED_NUMLOCK = 0, KB_LED_CAPSLOCK, KB_LED_SCROLLLOCK, KB_LED_COUNT, }; static const struct gpio_dt_spec kb_leds[KB_LED_COUNT] = { GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios, {0}), GPIO_DT_SPEC_GET_OR(DT_ALIAS(led1), gpios, {0}), GPIO_DT_SPEC_GET_OR(DT_ALIAS(led2), gpios, {0}), }; enum kb_report_idx { KB_MOD_KEY = 0, KB_RESERVED, KB_KEY_CODE1, KB_KEY_CODE2, KB_KEY_CODE3, KB_KEY_CODE4, KB_KEY_CODE5, KB_KEY_CODE6, KB_REPORT_COUNT, }; struct kb_event { uint16_t code; int32_t value; }; K_MSGQ_DEFINE(kb_msgq, sizeof(struct kb_event), 2, 1); UDC_STATIC_BUF_DEFINE(report, KB_REPORT_COUNT); static uint32_t kb_duration; static bool kb_ready; static void input_cb(struct input_event *evt, void *user_data) { struct kb_event kb_evt; ARG_UNUSED(user_data); kb_evt.code = evt->code; kb_evt.value = evt->value; if (k_msgq_put(&kb_msgq, &kb_evt, K_NO_WAIT) != 0) { LOG_ERR("Failed to put new input event"); } } INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL); static void kb_iface_ready(const struct device *dev, const bool ready) { LOG_INF("HID device %s interface is %s", dev->name, ready ? "ready" : "not ready"); kb_ready = ready; } static int kb_get_report(const struct device *dev, const uint8_t type, const uint8_t id, const uint16_t len, uint8_t *const buf) { LOG_WRN("Get Report not implemented, Type %u ID %u", type, id); return 0; } static int kb_set_report(const struct device *dev, const uint8_t type, const uint8_t id, const uint16_t len, const uint8_t *const buf) { if (type != HID_REPORT_TYPE_OUTPUT) { LOG_WRN("Unsupported report type"); return -ENOTSUP; } for (unsigned int i = 0; i < ARRAY_SIZE(kb_leds); i++) { if (kb_leds[i].port == NULL) { continue; } (void)gpio_pin_set_dt(&kb_leds[i], buf[0] & BIT(i)); } return 0; } /* Idle duration is stored but not used to calculate idle reports. */ static void kb_set_idle(const struct device *dev, const uint8_t id, const uint32_t duration) { LOG_INF("Set Idle %u to %u", id, duration); kb_duration = duration; } static uint32_t kb_get_idle(const struct device *dev, const uint8_t id) { LOG_INF("Get Idle %u to %u", id, kb_duration); return kb_duration; } static void kb_set_protocol(const struct device *dev, const uint8_t proto) { LOG_INF("Protocol changed to %s", proto == 0U ? "Boot Protocol" : "Report Protocol"); } static void kb_output_report(const struct device *dev, const uint16_t len, const uint8_t *const buf) { LOG_HEXDUMP_DBG(buf, len, "o.r."); kb_set_report(dev, HID_REPORT_TYPE_OUTPUT, 0U, len, buf); } struct hid_device_ops kb_ops = { .iface_ready = kb_iface_ready, .get_report = kb_get_report, .set_report = kb_set_report, .set_idle = kb_set_idle, .get_idle = kb_get_idle, .set_protocol = kb_set_protocol, .output_report = kb_output_report, }; /* doc device msg-cb start */ static void msg_cb(struct usbd_context *const usbd_ctx, const struct usbd_msg *const msg) { LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); if (msg->type == USBD_MSG_CONFIGURATION) { LOG_INF("\tConfiguration value %d", msg->status); } if (usbd_can_detect_vbus(usbd_ctx)) { if (msg->type == USBD_MSG_VBUS_READY) { if (usbd_enable(usbd_ctx)) { LOG_ERR("Failed to enable device support"); } } if (msg->type == USBD_MSG_VBUS_REMOVED) { if (usbd_disable(usbd_ctx)) { LOG_ERR("Failed to disable device support"); } } } } /* doc device msg-cb end */ int main(void) { struct usbd_context *sample_usbd; const struct device *hid_dev; int ret; for (unsigned int i = 0; i < ARRAY_SIZE(kb_leds); i++) { if (kb_leds[i].port == NULL) { continue; } if (!gpio_is_ready_dt(&kb_leds[i])) { LOG_ERR("LED device %s is not ready", kb_leds[i].port->name); return -EIO; } ret = gpio_pin_configure_dt(&kb_leds[i], GPIO_OUTPUT_INACTIVE); if (ret != 0) { LOG_ERR("Failed to configure the LED pin, %d", ret); return -EIO; } } hid_dev = DEVICE_DT_GET_ONE(zephyr_hid_device); if (!device_is_ready(hid_dev)) { LOG_ERR("HID Device is not ready"); return -EIO; } ret = hid_device_register(hid_dev, hid_report_desc, sizeof(hid_report_desc), &kb_ops); if (ret != 0) { LOG_ERR("Failed to register HID Device, %d", ret); return ret; } sample_usbd = sample_usbd_init_device(msg_cb); if (sample_usbd == NULL) { LOG_ERR("Failed to initialize USB device"); return -ENODEV; } if (!usbd_can_detect_vbus(sample_usbd)) { /* doc device enable start */ ret = usbd_enable(sample_usbd); if (ret) { LOG_ERR("Failed to enable device support"); return ret; } /* doc device enable end */ } LOG_INF("HID keyboard sample is initialized"); while (true) { struct kb_event kb_evt; k_msgq_get(&kb_msgq, &kb_evt, K_FOREVER); switch (kb_evt.code) { case INPUT_KEY_0: if (kb_evt.value) { report[KB_KEY_CODE1] = HID_KEY_NUMLOCK; } else { report[KB_KEY_CODE1] = 0; } break; case INPUT_KEY_1: if (kb_evt.value) { report[KB_KEY_CODE2] = HID_KEY_CAPSLOCK; } else { report[KB_KEY_CODE2] = 0; } break; case INPUT_KEY_2: if (kb_evt.value) { report[KB_KEY_CODE3] = HID_KEY_SCROLLLOCK; } else { report[KB_KEY_CODE3] = 0; } break; case INPUT_KEY_3: if (kb_evt.value) { report[KB_MOD_KEY] = HID_KBD_MODIFIER_RIGHT_ALT; report[KB_KEY_CODE4] = HID_KEY_1; report[KB_KEY_CODE5] = HID_KEY_2; report[KB_KEY_CODE6] = HID_KEY_3; } else { report[KB_MOD_KEY] = HID_KBD_MODIFIER_NONE; report[KB_KEY_CODE4] = 0; report[KB_KEY_CODE5] = 0; report[KB_KEY_CODE6] = 0; } break; default: LOG_INF("Unrecognized input code %u value %d", kb_evt.code, kb_evt.value); continue; } if (!kb_ready) { LOG_INF("USB HID device is not ready"); continue; } if (usbd_is_suspended(sample_usbd)) { /* on a press of any button, send wakeup request */ if (kb_evt.value) { ret = usbd_wakeup_request(sample_usbd); if (ret) { LOG_ERR("Remote wakeup error, %d", ret); } } continue; } ret = hid_device_submit_report(hid_dev, KB_REPORT_COUNT, report); if (ret) { LOG_ERR("HID submit report error, %d", ret); } } return 0; }