There is no need to use SYS_INIT as the HID specific initialisation can be done in the main before the usb_enable() call. Signed-off-by: Johann Fischer <johann.fischer@nordicsemi.no>
176 lines
3.9 KiB
C
176 lines
3.9 KiB
C
/*
|
|
* Copyright (c) 2016-2018 Intel Corporation.
|
|
* Copyright (c) 2018-2021 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/init.h>
|
|
|
|
#include <zephyr/usb/usb_device.h>
|
|
#include <zephyr/usb/class/usb_hid.h>
|
|
|
|
#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;
|
|
}
|