1 /*
2 * Copyright (c) 2016-2018 Intel Corporation.
3 * Copyright (c) 2018-2021 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/kernel.h>
9 #include <zephyr/init.h>
10
11 #include <zephyr/usb/usb_device.h>
12 #include <zephyr/usb/class/usb_hid.h>
13
14 #define LOG_LEVEL LOG_LEVEL_INF
15 LOG_MODULE_REGISTER(main);
16
17 static bool configured;
18 static const struct device *hdev;
19 static struct k_work report_send;
20 static ATOMIC_DEFINE(hid_ep_in_busy, 1);
21
22 #define HID_EP_BUSY_FLAG 0
23 #define REPORT_ID_1 0x01
24 #define REPORT_PERIOD K_SECONDS(2)
25
26 static struct report {
27 uint8_t id;
28 uint8_t value;
29 } __packed report_1 = {
30 .id = REPORT_ID_1,
31 .value = 0,
32 };
33
34 static void report_event_handler(struct k_timer *dummy);
35 static K_TIMER_DEFINE(event_timer, report_event_handler, NULL);
36
37 /*
38 * Simple HID Report Descriptor
39 * Report ID is present for completeness, although it can be omitted.
40 * Output of "usbhid-dump -d 2fe3:0006 -e descriptor":
41 * 05 01 09 00 A1 01 15 00 26 FF 00 85 01 75 08 95
42 * 01 09 00 81 02 C0
43 */
44 static const uint8_t hid_report_desc[] = {
45 HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
46 HID_USAGE(HID_USAGE_GEN_DESKTOP_UNDEFINED),
47 HID_COLLECTION(HID_COLLECTION_APPLICATION),
48 HID_LOGICAL_MIN8(0x00),
49 HID_LOGICAL_MAX16(0xFF, 0x00),
50 HID_REPORT_ID(REPORT_ID_1),
51 HID_REPORT_SIZE(8),
52 HID_REPORT_COUNT(1),
53 HID_USAGE(HID_USAGE_GEN_DESKTOP_UNDEFINED),
54 HID_INPUT(0x02),
55 HID_END_COLLECTION,
56 };
57
send_report(struct k_work * work)58 static void send_report(struct k_work *work)
59 {
60 int ret, wrote;
61
62 if (!atomic_test_and_set_bit(hid_ep_in_busy, HID_EP_BUSY_FLAG)) {
63 ret = hid_int_ep_write(hdev, (uint8_t *)&report_1,
64 sizeof(report_1), &wrote);
65 if (ret != 0) {
66 /*
67 * Do nothing and wait until host has reset the device
68 * and hid_ep_in_busy is cleared.
69 */
70 LOG_ERR("Failed to submit report");
71 } else {
72 LOG_DBG("Report submitted");
73 }
74 } else {
75 LOG_DBG("HID IN endpoint busy");
76 }
77 }
78
int_in_ready_cb(const struct device * dev)79 static void int_in_ready_cb(const struct device *dev)
80 {
81 ARG_UNUSED(dev);
82 if (!atomic_test_and_clear_bit(hid_ep_in_busy, HID_EP_BUSY_FLAG)) {
83 LOG_WRN("IN endpoint callback without preceding buffer write");
84 }
85 }
86
87 /*
88 * On Idle callback is available here as an example even if actual use is
89 * very limited. In contrast to report_event_handler(),
90 * report value is not incremented here.
91 */
on_idle_cb(const struct device * dev,uint16_t report_id)92 static void on_idle_cb(const struct device *dev, uint16_t report_id)
93 {
94 LOG_DBG("On idle callback");
95 k_work_submit(&report_send);
96 }
97
report_event_handler(struct k_timer * dummy)98 static void report_event_handler(struct k_timer *dummy)
99 {
100 /* Increment reported data */
101 report_1.value++;
102 k_work_submit(&report_send);
103 }
104
protocol_cb(const struct device * dev,uint8_t protocol)105 static void protocol_cb(const struct device *dev, uint8_t protocol)
106 {
107 LOG_INF("New protocol: %s", protocol == HID_PROTOCOL_BOOT ?
108 "boot" : "report");
109 }
110
111 static const struct hid_ops ops = {
112 .int_in_ready = int_in_ready_cb,
113 .on_idle = on_idle_cb,
114 .protocol_change = protocol_cb,
115 };
116
status_cb(enum usb_dc_status_code status,const uint8_t * param)117 static void status_cb(enum usb_dc_status_code status, const uint8_t *param)
118 {
119 switch (status) {
120 case USB_DC_RESET:
121 configured = false;
122 break;
123 case USB_DC_CONFIGURED:
124 if (!configured) {
125 int_in_ready_cb(hdev);
126 configured = true;
127 }
128 break;
129 case USB_DC_SOF:
130 break;
131 default:
132 LOG_DBG("status %u unhandled", status);
133 break;
134 }
135 }
136
main(void)137 int main(void)
138 {
139 int ret;
140
141 hdev = device_get_binding("HID_0");
142 if (hdev == NULL) {
143 LOG_ERR("Cannot get USB HID Device");
144 return -ENODEV;
145 }
146
147 LOG_INF("HID Device: dev %p", hdev);
148
149 usb_hid_register_device(hdev, hid_report_desc, sizeof(hid_report_desc),
150 &ops);
151
152 atomic_set_bit(hid_ep_in_busy, HID_EP_BUSY_FLAG);
153 k_timer_start(&event_timer, REPORT_PERIOD, REPORT_PERIOD);
154
155 if (usb_hid_set_proto_code(hdev, HID_BOOT_IFACE_CODE_NONE)) {
156 LOG_WRN("Failed to set Protocol Code");
157 }
158
159 ret = usb_hid_init(hdev);
160 if (ret != 0) {
161 return ret;
162 }
163
164 LOG_INF("Starting application");
165
166 ret = usb_enable(status_cb);
167 if (ret != 0) {
168 LOG_ERR("Failed to enable USB");
169 return ret;
170 }
171
172 k_work_init(&report_send, send_report);
173
174 return 0;
175 }
176