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 <string.h>
9
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/gpio.h>
13 #include <zephyr/input/input.h>
14 #include <zephyr/sys/util.h>
15
16 #include <zephyr/usb/usb_device.h>
17 #include <zephyr/usb/class/usb_hid.h>
18
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
21
22 static const struct gpio_dt_spec led0 = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
23 static const uint8_t hid_report_desc[] = HID_MOUSE_REPORT_DESC(2);
24 static enum usb_dc_status_code usb_status;
25
26 #define MOUSE_BTN_LEFT 0
27 #define MOUSE_BTN_RIGHT 1
28
29 enum mouse_report_idx {
30 MOUSE_BTN_REPORT_IDX = 0,
31 MOUSE_X_REPORT_IDX = 1,
32 MOUSE_Y_REPORT_IDX = 2,
33 MOUSE_WHEEL_REPORT_IDX = 3,
34 MOUSE_REPORT_COUNT = 4,
35 };
36
37 static uint8_t report[MOUSE_REPORT_COUNT];
38 static K_SEM_DEFINE(report_sem, 0, 1);
39
status_cb(enum usb_dc_status_code status,const uint8_t * param)40 static void status_cb(enum usb_dc_status_code status, const uint8_t *param)
41 {
42 usb_status = status;
43 }
44
rwup_if_suspended(void)45 static ALWAYS_INLINE void rwup_if_suspended(void)
46 {
47 if (IS_ENABLED(CONFIG_USB_DEVICE_REMOTE_WAKEUP)) {
48 if (usb_status == USB_DC_SUSPEND) {
49 usb_wakeup_request();
50 return;
51 }
52 }
53 }
54
input_cb(struct input_event * evt)55 static void input_cb(struct input_event *evt)
56 {
57 uint8_t tmp[MOUSE_REPORT_COUNT];
58
59 (void)memcpy(tmp, report, sizeof(tmp));
60
61 switch (evt->code) {
62 case INPUT_KEY_0:
63 rwup_if_suspended();
64 WRITE_BIT(tmp[MOUSE_BTN_REPORT_IDX], MOUSE_BTN_LEFT, evt->value);
65 break;
66 case INPUT_KEY_1:
67 rwup_if_suspended();
68 WRITE_BIT(tmp[MOUSE_BTN_REPORT_IDX], MOUSE_BTN_RIGHT, evt->value);
69 break;
70 case INPUT_KEY_2:
71 if (evt->value) {
72 tmp[MOUSE_X_REPORT_IDX] += 10U;
73 }
74
75 break;
76 case INPUT_KEY_3:
77 if (evt->value) {
78 tmp[MOUSE_Y_REPORT_IDX] += 10U;
79 }
80
81 break;
82 default:
83 LOG_INF("Unrecognized input code %u value %d",
84 evt->code, evt->value);
85 return;
86 }
87
88 if (memcmp(tmp, report, sizeof(tmp))) {
89 memcpy(report, tmp, sizeof(report));
90 k_sem_give(&report_sem);
91 }
92 }
93
94 INPUT_CALLBACK_DEFINE(NULL, input_cb);
95
main(void)96 int main(void)
97 {
98 const struct device *hid_dev;
99 int ret;
100
101 if (!gpio_is_ready_dt(&led0)) {
102 LOG_ERR("LED device %s is not ready", led0.port->name);
103 return 0;
104 }
105
106 hid_dev = device_get_binding("HID_0");
107 if (hid_dev == NULL) {
108 LOG_ERR("Cannot get USB HID Device");
109 return 0;
110 }
111
112 ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT);
113 if (ret < 0) {
114 LOG_ERR("Failed to configure the LED pin, error: %d", ret);
115 return 0;
116 }
117
118 usb_hid_register_device(hid_dev,
119 hid_report_desc, sizeof(hid_report_desc),
120 NULL);
121
122 usb_hid_init(hid_dev);
123
124 ret = usb_enable(status_cb);
125 if (ret != 0) {
126 LOG_ERR("Failed to enable USB");
127 return 0;
128 }
129
130 while (true) {
131 k_sem_take(&report_sem, K_FOREVER);
132
133 ret = hid_int_ep_write(hid_dev, report, sizeof(report), NULL);
134 report[MOUSE_X_REPORT_IDX] = 0U;
135 report[MOUSE_Y_REPORT_IDX] = 0U;
136 if (ret) {
137 LOG_ERR("HID write error, %d", ret);
138 }
139
140 /* Toggle LED on sent report */
141 ret = gpio_pin_toggle(led0.port, led0.pin);
142 if (ret < 0) {
143 LOG_ERR("Failed to toggle the LED pin, error: %d", ret);
144 }
145 }
146 return 0;
147 }
148