1 /*
2  * Copyright (c) 2018 qianfan Zhao
3  * Copyright (c) 2018, 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <sample_usbd.h>
9 
10 #include <string.h>
11 
12 #include <zephyr/kernel.h>
13 #include <zephyr/device.h>
14 #include <zephyr/drivers/gpio.h>
15 #include <zephyr/input/input.h>
16 #include <zephyr/sys/util.h>
17 
18 #include <zephyr/usb/usbd.h>
19 #include <zephyr/usb/class/usbd_hid.h>
20 
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
23 
24 static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
25 static const uint8_t hid_report_desc[] = HID_MOUSE_REPORT_DESC(2);
26 
27 #define MOUSE_BTN_LEFT		0
28 #define MOUSE_BTN_RIGHT		1
29 
30 enum mouse_report_idx {
31 	MOUSE_BTN_REPORT_IDX = 0,
32 	MOUSE_X_REPORT_IDX = 1,
33 	MOUSE_Y_REPORT_IDX = 2,
34 	MOUSE_WHEEL_REPORT_IDX = 3,
35 	MOUSE_REPORT_COUNT = 4,
36 };
37 
38 K_MSGQ_DEFINE(mouse_msgq, MOUSE_REPORT_COUNT, 2, 1);
39 static bool mouse_ready;
40 
input_cb(struct input_event * evt,void * user_data)41 static void input_cb(struct input_event *evt, void *user_data)
42 {
43 	static uint8_t tmp[MOUSE_REPORT_COUNT];
44 
45 	ARG_UNUSED(user_data);
46 
47 	switch (evt->code) {
48 	case INPUT_KEY_0:
49 		WRITE_BIT(tmp[MOUSE_BTN_REPORT_IDX], MOUSE_BTN_LEFT, evt->value);
50 		break;
51 	case INPUT_KEY_1:
52 		WRITE_BIT(tmp[MOUSE_BTN_REPORT_IDX], MOUSE_BTN_RIGHT, evt->value);
53 		break;
54 	case INPUT_KEY_2:
55 		if (evt->value) {
56 			tmp[MOUSE_X_REPORT_IDX] += 10U;
57 		}
58 
59 		break;
60 	case INPUT_KEY_3:
61 		if (evt->value) {
62 			tmp[MOUSE_Y_REPORT_IDX] += 10U;
63 		}
64 
65 		break;
66 	default:
67 		LOG_INF("Unrecognized input code %u value %d",
68 			evt->code, evt->value);
69 		return;
70 	}
71 
72 	if (k_msgq_put(&mouse_msgq, tmp, K_NO_WAIT) != 0) {
73 		LOG_ERR("Failed to put new input event");
74 	}
75 
76 	tmp[MOUSE_X_REPORT_IDX] = 0U;
77 	tmp[MOUSE_Y_REPORT_IDX] = 0U;
78 
79 }
80 
81 INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL);
82 
mouse_iface_ready(const struct device * dev,const bool ready)83 static void mouse_iface_ready(const struct device *dev, const bool ready)
84 {
85 	LOG_INF("HID device %s interface is %s",
86 		dev->name, ready ? "ready" : "not ready");
87 	mouse_ready = ready;
88 }
89 
mouse_get_report(const struct device * dev,const uint8_t type,const uint8_t id,const uint16_t len,uint8_t * const buf)90 static int mouse_get_report(const struct device *dev,
91 			 const uint8_t type, const uint8_t id, const uint16_t len,
92 			 uint8_t *const buf)
93 {
94 	LOG_WRN("Get Report not implemented, Type %u ID %u", type, id);
95 
96 	return 0;
97 }
98 
99 struct hid_device_ops mouse_ops = {
100 	.iface_ready = mouse_iface_ready,
101 	.get_report = mouse_get_report,
102 };
103 
main(void)104 int main(void)
105 {
106 	struct usbd_context *sample_usbd;
107 	const struct device *hid_dev;
108 	int ret;
109 
110 	if (!gpio_is_ready_dt(&led0)) {
111 		LOG_ERR("LED device %s is not ready", led0.port->name);
112 		return 0;
113 	}
114 
115 	ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT);
116 	if (ret != 0) {
117 		LOG_ERR("Failed to configure the LED pin, error: %d", ret);
118 		return 0;
119 	}
120 
121 	hid_dev = DEVICE_DT_GET_ONE(zephyr_hid_device);
122 	if (!device_is_ready(hid_dev)) {
123 		LOG_ERR("HID Device is not ready");
124 		return -EIO;
125 	}
126 
127 	ret = hid_device_register(hid_dev,
128 				  hid_report_desc, sizeof(hid_report_desc),
129 				  &mouse_ops);
130 	if (ret != 0) {
131 		LOG_ERR("Failed to register HID Device, %d", ret);
132 		return ret;
133 	}
134 
135 	sample_usbd = sample_usbd_init_device(NULL);
136 	if (sample_usbd == NULL) {
137 		LOG_ERR("Failed to initialize USB device");
138 		return -ENODEV;
139 	}
140 
141 	ret = usbd_enable(sample_usbd);
142 	if (ret != 0) {
143 		LOG_ERR("Failed to enable device support");
144 		return ret;
145 	}
146 
147 	LOG_DBG("USB device support enabled");
148 
149 	while (true) {
150 		UDC_STATIC_BUF_DEFINE(report, MOUSE_REPORT_COUNT);
151 
152 		k_msgq_get(&mouse_msgq, &report, K_FOREVER);
153 
154 		if (!mouse_ready) {
155 			LOG_INF("USB HID device is not ready");
156 			continue;
157 		}
158 
159 		ret = hid_device_submit_report(hid_dev, MOUSE_REPORT_COUNT, report);
160 		if (ret) {
161 			LOG_ERR("HID submit report error, %d", ret);
162 		} else {
163 			/* Toggle LED on sent report */
164 			(void)gpio_pin_toggle(led0.port, led0.pin);
165 		}
166 	}
167 
168 	return 0;
169 }
170