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/usb_device.h>
19 #include <zephyr/usb/usbd.h>
20 #include <zephyr/usb/class/usb_hid.h>
21 
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
24 
25 static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
26 static const uint8_t hid_report_desc[] = HID_MOUSE_REPORT_DESC(2);
27 static enum usb_dc_status_code usb_status;
28 
29 #define MOUSE_BTN_LEFT		0
30 #define MOUSE_BTN_RIGHT		1
31 
32 enum mouse_report_idx {
33 	MOUSE_BTN_REPORT_IDX = 0,
34 	MOUSE_X_REPORT_IDX = 1,
35 	MOUSE_Y_REPORT_IDX = 2,
36 	MOUSE_WHEEL_REPORT_IDX = 3,
37 	MOUSE_REPORT_COUNT = 4,
38 };
39 
40 K_MSGQ_DEFINE(mouse_msgq, MOUSE_REPORT_COUNT, 2, 1);
41 static K_SEM_DEFINE(ep_write_sem, 0, 1);
42 
status_cb(enum usb_dc_status_code status,const uint8_t * param)43 static inline void status_cb(enum usb_dc_status_code status, const uint8_t *param)
44 {
45 	usb_status = status;
46 }
47 
rwup_if_suspended(void)48 static ALWAYS_INLINE void rwup_if_suspended(void)
49 {
50 	if (IS_ENABLED(CONFIG_USB_DEVICE_REMOTE_WAKEUP)) {
51 		if (usb_status == USB_DC_SUSPEND) {
52 			usb_wakeup_request();
53 			return;
54 		}
55 	}
56 }
57 
input_cb(struct input_event * evt,void * user_data)58 static void input_cb(struct input_event *evt, void *user_data)
59 {
60 	static uint8_t tmp[MOUSE_REPORT_COUNT];
61 
62 	ARG_UNUSED(user_data);
63 
64 	switch (evt->code) {
65 	case INPUT_KEY_0:
66 		rwup_if_suspended();
67 		WRITE_BIT(tmp[MOUSE_BTN_REPORT_IDX], MOUSE_BTN_LEFT, evt->value);
68 		break;
69 	case INPUT_KEY_1:
70 		rwup_if_suspended();
71 		WRITE_BIT(tmp[MOUSE_BTN_REPORT_IDX], MOUSE_BTN_RIGHT, evt->value);
72 		break;
73 	case INPUT_KEY_2:
74 		if (evt->value) {
75 			tmp[MOUSE_X_REPORT_IDX] += 10U;
76 		}
77 
78 		break;
79 	case INPUT_KEY_3:
80 		if (evt->value) {
81 			tmp[MOUSE_Y_REPORT_IDX] += 10U;
82 		}
83 
84 		break;
85 	default:
86 		LOG_INF("Unrecognized input code %u value %d",
87 			evt->code, evt->value);
88 		return;
89 	}
90 
91 	if (k_msgq_put(&mouse_msgq, tmp, K_NO_WAIT) != 0) {
92 		LOG_ERR("Failed to put new input event");
93 	}
94 
95 	tmp[MOUSE_X_REPORT_IDX] = 0U;
96 	tmp[MOUSE_Y_REPORT_IDX] = 0U;
97 
98 }
99 
100 INPUT_CALLBACK_DEFINE(NULL, input_cb, NULL);
101 
102 #if defined(CONFIG_USB_DEVICE_STACK_NEXT)
enable_usb_device_next(void)103 static int enable_usb_device_next(void)
104 {
105 	struct usbd_context *sample_usbd;
106 	int err;
107 
108 	sample_usbd = sample_usbd_init_device(NULL);
109 	if (sample_usbd == NULL) {
110 		LOG_ERR("Failed to initialize USB device");
111 		return -ENODEV;
112 	}
113 
114 	err = usbd_enable(sample_usbd);
115 	if (err) {
116 		LOG_ERR("Failed to enable device support");
117 		return err;
118 	}
119 
120 	LOG_DBG("USB device support enabled");
121 
122 	return 0;
123 }
124 #endif /* defined(CONFIG_USB_DEVICE_STACK_NEXT) */
125 
int_in_ready_cb(const struct device * dev)126 static void int_in_ready_cb(const struct device *dev)
127 {
128 	ARG_UNUSED(dev);
129 	k_sem_give(&ep_write_sem);
130 }
131 
132 static const struct hid_ops ops = {
133 	.int_in_ready = int_in_ready_cb,
134 };
135 
main(void)136 int main(void)
137 {
138 	const struct device *hid_dev;
139 	int ret;
140 
141 	if (!gpio_is_ready_dt(&led0)) {
142 		LOG_ERR("LED device %s is not ready", led0.port->name);
143 		return 0;
144 	}
145 
146 #if defined(CONFIG_USB_DEVICE_STACK_NEXT)
147 	hid_dev = DEVICE_DT_GET_ONE(zephyr_hid_device);
148 #else
149 	hid_dev = device_get_binding("HID_0");
150 #endif
151 	if (hid_dev == NULL) {
152 		LOG_ERR("Cannot get USB HID Device");
153 		return 0;
154 	}
155 
156 	ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT);
157 	if (ret < 0) {
158 		LOG_ERR("Failed to configure the LED pin, error: %d", ret);
159 		return 0;
160 	}
161 
162 	usb_hid_register_device(hid_dev,
163 				hid_report_desc, sizeof(hid_report_desc),
164 				&ops);
165 
166 	usb_hid_init(hid_dev);
167 
168 #if defined(CONFIG_USB_DEVICE_STACK_NEXT)
169 	ret = enable_usb_device_next();
170 #else
171 	ret = usb_enable(status_cb);
172 #endif
173 	if (ret != 0) {
174 		LOG_ERR("Failed to enable USB");
175 		return 0;
176 	}
177 
178 	while (true) {
179 		UDC_STATIC_BUF_DEFINE(report, MOUSE_REPORT_COUNT);
180 
181 		k_msgq_get(&mouse_msgq, &report, K_FOREVER);
182 
183 		ret = hid_int_ep_write(hid_dev, report, MOUSE_REPORT_COUNT, NULL);
184 		if (ret) {
185 			LOG_ERR("HID write error, %d", ret);
186 		} else {
187 			k_sem_take(&ep_write_sem, K_FOREVER);
188 			/* Toggle LED on sent report */
189 			(void)gpio_pin_toggle(led0.port, led0.pin);
190 		}
191 	}
192 	return 0;
193 }
194