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