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