/* * Copyright (c) 2016-2018 Intel Corporation. * Copyright (c) 2018-2021 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #define LOG_LEVEL LOG_LEVEL_INF LOG_MODULE_REGISTER(main); static bool configured; static const struct device *hdev; static struct k_work report_send; static ATOMIC_DEFINE(hid_ep_in_busy, 1); #define HID_EP_BUSY_FLAG 0 #define REPORT_ID_1 0x01 #define REPORT_PERIOD K_SECONDS(2) static struct report { uint8_t id; uint8_t value; } __packed report_1 = { .id = REPORT_ID_1, .value = 0, }; static void report_event_handler(struct k_timer *dummy); static K_TIMER_DEFINE(event_timer, report_event_handler, NULL); /* * Simple HID Report Descriptor * Report ID is present for completeness, although it can be omitted. * Output of "usbhid-dump -d 2fe3:0006 -e descriptor": * 05 01 09 00 A1 01 15 00 26 FF 00 85 01 75 08 95 * 01 09 00 81 02 C0 */ static const uint8_t hid_report_desc[] = { HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP), HID_USAGE(HID_USAGE_GEN_DESKTOP_UNDEFINED), HID_COLLECTION(HID_COLLECTION_APPLICATION), HID_LOGICAL_MIN8(0x00), HID_LOGICAL_MAX16(0xFF, 0x00), HID_REPORT_ID(REPORT_ID_1), HID_REPORT_SIZE(8), HID_REPORT_COUNT(1), HID_USAGE(HID_USAGE_GEN_DESKTOP_UNDEFINED), HID_INPUT(0x02), HID_END_COLLECTION, }; static void send_report(struct k_work *work) { int ret, wrote; if (!atomic_test_and_set_bit(hid_ep_in_busy, HID_EP_BUSY_FLAG)) { ret = hid_int_ep_write(hdev, (uint8_t *)&report_1, sizeof(report_1), &wrote); if (ret != 0) { /* * Do nothing and wait until host has reset the device * and hid_ep_in_busy is cleared. */ LOG_ERR("Failed to submit report"); } else { LOG_DBG("Report submitted"); } } else { LOG_DBG("HID IN endpoint busy"); } } static void int_in_ready_cb(const struct device *dev) { ARG_UNUSED(dev); if (!atomic_test_and_clear_bit(hid_ep_in_busy, HID_EP_BUSY_FLAG)) { LOG_WRN("IN endpoint callback without preceding buffer write"); } } /* * On Idle callback is available here as an example even if actual use is * very limited. In contrast to report_event_handler(), * report value is not incremented here. */ static void on_idle_cb(const struct device *dev, uint16_t report_id) { LOG_DBG("On idle callback"); k_work_submit(&report_send); } static void report_event_handler(struct k_timer *dummy) { /* Increment reported data */ report_1.value++; k_work_submit(&report_send); } static void protocol_cb(const struct device *dev, uint8_t protocol) { LOG_INF("New protocol: %s", protocol == HID_PROTOCOL_BOOT ? "boot" : "report"); } static const struct hid_ops ops = { .int_in_ready = int_in_ready_cb, .on_idle = on_idle_cb, .protocol_change = protocol_cb, }; static void status_cb(enum usb_dc_status_code status, const uint8_t *param) { switch (status) { case USB_DC_RESET: configured = false; break; case USB_DC_CONFIGURED: if (!configured) { int_in_ready_cb(hdev); configured = true; } break; case USB_DC_SOF: break; default: LOG_DBG("status %u unhandled", status); break; } } int main(void) { int ret; hdev = device_get_binding("HID_0"); if (hdev == NULL) { LOG_ERR("Cannot get USB HID Device"); return -ENODEV; } LOG_INF("HID Device: dev %p", hdev); usb_hid_register_device(hdev, hid_report_desc, sizeof(hid_report_desc), &ops); atomic_set_bit(hid_ep_in_busy, HID_EP_BUSY_FLAG); k_timer_start(&event_timer, REPORT_PERIOD, REPORT_PERIOD); if (usb_hid_set_proto_code(hdev, HID_BOOT_IFACE_CODE_NONE)) { LOG_WRN("Failed to set Protocol Code"); } ret = usb_hid_init(hdev); if (ret != 0) { return ret; } LOG_INF("Starting application"); ret = usb_enable(status_cb); if (ret != 0) { LOG_ERR("Failed to enable USB"); return ret; } k_work_init(&report_send, send_report); return 0; }