1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <sample_usbd.h>
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/input/input.h>
13
14 #include <zephyr/usb/usbd.h>
15 #include <zephyr/usb/class/usbd_hid.h>
16
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
19
20 static const uint8_t hid_report_desc[] = HID_KEYBOARD_REPORT_DESC();
21
22 enum kb_leds_idx {
23 KB_LED_NUMLOCK = 0,
24 KB_LED_CAPSLOCK,
25 KB_LED_SCROLLLOCK,
26 KB_LED_COUNT,
27 };
28
29 static const struct gpio_dt_spec kb_leds[KB_LED_COUNT] = {
30 GPIO_DT_SPEC_GET_OR(DT_ALIAS(led0), gpios, {0}),
31 GPIO_DT_SPEC_GET_OR(DT_ALIAS(led1), gpios, {0}),
32 GPIO_DT_SPEC_GET_OR(DT_ALIAS(led2), gpios, {0}),
33 };
34
35 enum kb_report_idx {
36 KB_MOD_KEY = 0,
37 KB_RESERVED,
38 KB_KEY_CODE1,
39 KB_KEY_CODE2,
40 KB_KEY_CODE3,
41 KB_KEY_CODE4,
42 KB_KEY_CODE5,
43 KB_KEY_CODE6,
44 KB_REPORT_COUNT,
45 };
46
47 struct kb_event {
48 uint16_t code;
49 int32_t value;
50 };
51
52 K_MSGQ_DEFINE(kb_msgq, sizeof(struct kb_event), 2, 1);
53
54 UDC_STATIC_BUF_DEFINE(report, KB_REPORT_COUNT);
55 static uint32_t kb_duration;
56 static bool kb_ready;
57
input_cb(struct input_event * evt,void * user_data)58 static void input_cb(struct input_event *evt, void *user_data)
59 {
60 struct kb_event kb_evt;
61
62 ARG_UNUSED(user_data);
63
64 kb_evt.code = evt->code;
65 kb_evt.value = evt->value;
66 if (k_msgq_put(&kb_msgq, &kb_evt, K_NO_WAIT) != 0) {
67 LOG_ERR("Failed to put new input event");
68 }
69 }
70
71 INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL);
72
kb_iface_ready(const struct device * dev,const bool ready)73 static void kb_iface_ready(const struct device *dev, const bool ready)
74 {
75 LOG_INF("HID device %s interface is %s",
76 dev->name, ready ? "ready" : "not ready");
77 kb_ready = ready;
78 }
79
kb_get_report(const struct device * dev,const uint8_t type,const uint8_t id,const uint16_t len,uint8_t * const buf)80 static int kb_get_report(const struct device *dev,
81 const uint8_t type, const uint8_t id, const uint16_t len,
82 uint8_t *const buf)
83 {
84 LOG_WRN("Get Report not implemented, Type %u ID %u", type, id);
85
86 return 0;
87 }
88
kb_set_report(const struct device * dev,const uint8_t type,const uint8_t id,const uint16_t len,const uint8_t * const buf)89 static int kb_set_report(const struct device *dev,
90 const uint8_t type, const uint8_t id, const uint16_t len,
91 const uint8_t *const buf)
92 {
93 if (type != HID_REPORT_TYPE_OUTPUT) {
94 LOG_WRN("Unsupported report type");
95 return -ENOTSUP;
96 }
97
98 for (unsigned int i = 0; i < ARRAY_SIZE(kb_leds); i++) {
99 if (kb_leds[i].port == NULL) {
100 continue;
101 }
102
103 (void)gpio_pin_set_dt(&kb_leds[i], buf[0] & BIT(i));
104 }
105
106 return 0;
107 }
108
109 /* Idle duration is stored but not used to calculate idle reports. */
kb_set_idle(const struct device * dev,const uint8_t id,const uint32_t duration)110 static void kb_set_idle(const struct device *dev,
111 const uint8_t id, const uint32_t duration)
112 {
113 LOG_INF("Set Idle %u to %u", id, duration);
114 kb_duration = duration;
115 }
116
kb_get_idle(const struct device * dev,const uint8_t id)117 static uint32_t kb_get_idle(const struct device *dev, const uint8_t id)
118 {
119 LOG_INF("Get Idle %u to %u", id, kb_duration);
120 return kb_duration;
121 }
122
kb_set_protocol(const struct device * dev,const uint8_t proto)123 static void kb_set_protocol(const struct device *dev, const uint8_t proto)
124 {
125 LOG_INF("Protocol changed to %s",
126 proto == 0U ? "Boot Protocol" : "Report Protocol");
127 }
128
kb_output_report(const struct device * dev,const uint16_t len,const uint8_t * const buf)129 static void kb_output_report(const struct device *dev, const uint16_t len,
130 const uint8_t *const buf)
131 {
132 LOG_HEXDUMP_DBG(buf, len, "o.r.");
133 kb_set_report(dev, HID_REPORT_TYPE_OUTPUT, 0U, len, buf);
134 }
135
136 struct hid_device_ops kb_ops = {
137 .iface_ready = kb_iface_ready,
138 .get_report = kb_get_report,
139 .set_report = kb_set_report,
140 .set_idle = kb_set_idle,
141 .get_idle = kb_get_idle,
142 .set_protocol = kb_set_protocol,
143 .output_report = kb_output_report,
144 };
145
146 /* doc device msg-cb start */
msg_cb(struct usbd_context * const usbd_ctx,const struct usbd_msg * const msg)147 static void msg_cb(struct usbd_context *const usbd_ctx,
148 const struct usbd_msg *const msg)
149 {
150 LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type));
151
152 if (msg->type == USBD_MSG_CONFIGURATION) {
153 LOG_INF("\tConfiguration value %d", msg->status);
154 }
155
156 if (usbd_can_detect_vbus(usbd_ctx)) {
157 if (msg->type == USBD_MSG_VBUS_READY) {
158 if (usbd_enable(usbd_ctx)) {
159 LOG_ERR("Failed to enable device support");
160 }
161 }
162
163 if (msg->type == USBD_MSG_VBUS_REMOVED) {
164 if (usbd_disable(usbd_ctx)) {
165 LOG_ERR("Failed to disable device support");
166 }
167 }
168 }
169 }
170 /* doc device msg-cb end */
171
main(void)172 int main(void)
173 {
174 struct usbd_context *sample_usbd;
175 const struct device *hid_dev;
176 int ret;
177
178 for (unsigned int i = 0; i < ARRAY_SIZE(kb_leds); i++) {
179 if (kb_leds[i].port == NULL) {
180 continue;
181 }
182
183 if (!gpio_is_ready_dt(&kb_leds[i])) {
184 LOG_ERR("LED device %s is not ready", kb_leds[i].port->name);
185 return -EIO;
186 }
187
188 ret = gpio_pin_configure_dt(&kb_leds[i], GPIO_OUTPUT_INACTIVE);
189 if (ret != 0) {
190 LOG_ERR("Failed to configure the LED pin, %d", ret);
191 return -EIO;
192 }
193 }
194
195 hid_dev = DEVICE_DT_GET_ONE(zephyr_hid_device);
196 if (!device_is_ready(hid_dev)) {
197 LOG_ERR("HID Device is not ready");
198 return -EIO;
199 }
200
201 ret = hid_device_register(hid_dev,
202 hid_report_desc, sizeof(hid_report_desc),
203 &kb_ops);
204 if (ret != 0) {
205 LOG_ERR("Failed to register HID Device, %d", ret);
206 return ret;
207 }
208
209 sample_usbd = sample_usbd_init_device(msg_cb);
210 if (sample_usbd == NULL) {
211 LOG_ERR("Failed to initialize USB device");
212 return -ENODEV;
213 }
214
215 if (!usbd_can_detect_vbus(sample_usbd)) {
216 /* doc device enable start */
217 ret = usbd_enable(sample_usbd);
218 if (ret) {
219 LOG_ERR("Failed to enable device support");
220 return ret;
221 }
222 /* doc device enable end */
223 }
224
225 LOG_INF("HID keyboard sample is initialized");
226
227 while (true) {
228 struct kb_event kb_evt;
229
230 k_msgq_get(&kb_msgq, &kb_evt, K_FOREVER);
231
232 switch (kb_evt.code) {
233 case INPUT_KEY_0:
234 if (kb_evt.value) {
235 report[KB_KEY_CODE1] = HID_KEY_NUMLOCK;
236 } else {
237 report[KB_KEY_CODE1] = 0;
238 }
239
240 break;
241 case INPUT_KEY_1:
242 if (kb_evt.value) {
243 report[KB_KEY_CODE2] = HID_KEY_CAPSLOCK;
244 } else {
245 report[KB_KEY_CODE2] = 0;
246 }
247
248 break;
249 case INPUT_KEY_2:
250 if (kb_evt.value) {
251 report[KB_KEY_CODE3] = HID_KEY_SCROLLLOCK;
252 } else {
253 report[KB_KEY_CODE3] = 0;
254 }
255
256 break;
257 case INPUT_KEY_3:
258 if (kb_evt.value) {
259 report[KB_MOD_KEY] = HID_KBD_MODIFIER_RIGHT_ALT;
260 report[KB_KEY_CODE4] = HID_KEY_1;
261 report[KB_KEY_CODE5] = HID_KEY_2;
262 report[KB_KEY_CODE6] = HID_KEY_3;
263 } else {
264 report[KB_MOD_KEY] = HID_KBD_MODIFIER_NONE;
265 report[KB_KEY_CODE4] = 0;
266 report[KB_KEY_CODE5] = 0;
267 report[KB_KEY_CODE6] = 0;
268 }
269
270 break;
271 default:
272 LOG_INF("Unrecognized input code %u value %d",
273 kb_evt.code, kb_evt.value);
274 continue;
275 }
276
277 if (!kb_ready) {
278 LOG_INF("USB HID device is not ready");
279 continue;
280 }
281
282 if (usbd_is_suspended(sample_usbd)) {
283 /* on a press of any button, send wakeup request */
284 if (kb_evt.value) {
285 ret = usbd_wakeup_request(sample_usbd);
286 if (ret) {
287 LOG_ERR("Remote wakeup error, %d", ret);
288 }
289 }
290 continue;
291 }
292
293 ret = hid_device_submit_report(hid_dev, KB_REPORT_COUNT, report);
294 if (ret) {
295 LOG_ERR("HID submit report error, %d", ret);
296 }
297 }
298
299 return 0;
300 }
301