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