1 /*
2 * Copyright (c) 2022, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "usbh_core.h"
7 #include "usbh_hid.h"
8
9 #define DEV_FORMAT "/dev/input%d"
10
11 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_hid_buf[128];
12
13 static struct usbh_hid g_hid_class[CONFIG_USBHOST_MAX_HID_CLASS];
14 static uint32_t g_devinuse = 0;
15
usbh_hid_class_alloc(void)16 static struct usbh_hid *usbh_hid_class_alloc(void)
17 {
18 int devno;
19
20 for (devno = 0; devno < CONFIG_USBHOST_MAX_HID_CLASS; devno++) {
21 if ((g_devinuse & (1 << devno)) == 0) {
22 g_devinuse |= (1 << devno);
23 memset(&g_hid_class[devno], 0, sizeof(struct usbh_hid));
24 g_hid_class[devno].minor = devno;
25 return &g_hid_class[devno];
26 }
27 }
28 return NULL;
29 }
30
usbh_hid_class_free(struct usbh_hid * hid_class)31 static void usbh_hid_class_free(struct usbh_hid *hid_class)
32 {
33 int devno = hid_class->minor;
34
35 if (devno >= 0 && devno < 32) {
36 g_devinuse &= ~(1 << devno);
37 }
38 memset(hid_class, 0, sizeof(struct usbh_hid));
39 }
40
usbh_hid_get_report_descriptor(struct usbh_hid * hid_class,uint8_t * buffer)41 static int usbh_hid_get_report_descriptor(struct usbh_hid *hid_class, uint8_t *buffer)
42 {
43 struct usb_setup_packet *setup = hid_class->hport->setup;
44 int ret;
45
46 setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_STANDARD | USB_REQUEST_RECIPIENT_INTERFACE;
47 setup->bRequest = USB_REQUEST_GET_DESCRIPTOR;
48 setup->wValue = HID_DESCRIPTOR_TYPE_HID_REPORT << 8;
49 setup->wIndex = hid_class->intf;
50 setup->wLength = 128;
51
52 ret = usbh_control_transfer(hid_class->hport->ep0, setup, g_hid_buf);
53 if (ret < 0) {
54 return ret;
55 }
56 memcpy(buffer, g_hid_buf, ret - 8);
57 return ret;
58 }
59
usbh_hid_set_idle(struct usbh_hid * hid_class,uint8_t report_id,uint8_t duration)60 int usbh_hid_set_idle(struct usbh_hid *hid_class, uint8_t report_id, uint8_t duration)
61 {
62 struct usb_setup_packet *setup = hid_class->hport->setup;
63
64 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
65 setup->bRequest = HID_REQUEST_SET_IDLE;
66 setup->wValue = (duration << 8) | report_id;
67 setup->wIndex = hid_class->intf;
68 setup->wLength = 0;
69
70 return usbh_control_transfer(hid_class->hport->ep0, setup, NULL);
71 }
72
usbh_hid_get_idle(struct usbh_hid * hid_class,uint8_t * buffer)73 int usbh_hid_get_idle(struct usbh_hid *hid_class, uint8_t *buffer)
74 {
75 struct usb_setup_packet *setup = hid_class->hport->setup;
76 int ret;
77
78 setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
79 setup->bRequest = HID_REQUEST_GET_IDLE;
80 setup->wValue = 0;
81 setup->wIndex = hid_class->intf;
82 setup->wLength = 1;
83
84 ret = usbh_control_transfer(hid_class->hport->ep0, setup, g_hid_buf);
85 if (ret < 0) {
86 return ret;
87 }
88 memcpy(buffer, g_hid_buf, 1);
89 return ret;
90 }
91
usbh_hid_set_protocol(struct usbh_hid * hid_class,uint8_t protocol)92 int usbh_hid_set_protocol(struct usbh_hid *hid_class, uint8_t protocol)
93 {
94 struct usb_setup_packet *setup = hid_class->hport->setup;
95
96 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_INTERFACE;
97 setup->bRequest = HID_REQUEST_SET_PROTOCOL;
98 setup->wValue = protocol;
99 setup->wIndex = 0;
100 setup->wLength = 0;
101
102 return usbh_control_transfer(hid_class->hport->ep0, setup, NULL);
103 }
104
usbh_hid_connect(struct usbh_hubport * hport,uint8_t intf)105 int usbh_hid_connect(struct usbh_hubport *hport, uint8_t intf)
106 {
107 struct usb_endpoint_descriptor *ep_desc;
108 int ret;
109
110 struct usbh_hid *hid_class = usbh_hid_class_alloc();
111 if (hid_class == NULL) {
112 USB_LOG_ERR("Fail to alloc hid_class\r\n");
113 return -ENOMEM;
114 }
115
116 hid_class->hport = hport;
117 hid_class->intf = intf;
118
119 hport->config.intf[intf].priv = hid_class;
120
121 // /* 0x0 = boot protocol, 0x1 = report protocol */
122 // ret = usbh_hid_set_protocol(hid_class, 0x1);
123 // if (ret < 0) {
124 // return ret;
125 // }
126
127 ret = usbh_hid_set_idle(hid_class, 0, 0);
128 if (ret < 0) {
129 USB_LOG_WRN("Do not support set idle\r\n");
130 }
131
132 ret = usbh_hid_get_report_descriptor(hid_class, hid_class->report_desc);
133 if (ret < 0) {
134 return ret;
135 }
136
137 for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
138 ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
139 if (ep_desc->bEndpointAddress & 0x80) {
140 usbh_hport_activate_epx(&hid_class->intin, hport, ep_desc);
141 } else {
142 usbh_hport_activate_epx(&hid_class->intout, hport, ep_desc);
143 }
144 }
145
146 snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, hid_class->minor);
147
148 USB_LOG_INFO("Register HID Class:%s\r\n", hport->config.intf[intf].devname);
149
150 usbh_hid_run(hid_class);
151 return ret;
152 }
153
usbh_hid_disconnect(struct usbh_hubport * hport,uint8_t intf)154 int usbh_hid_disconnect(struct usbh_hubport *hport, uint8_t intf)
155 {
156 int ret = 0;
157
158 struct usbh_hid *hid_class = (struct usbh_hid *)hport->config.intf[intf].priv;
159
160 if (hid_class) {
161 if (hid_class->intin) {
162 usbh_pipe_free(hid_class->intin);
163 }
164
165 if (hid_class->intout) {
166 usbh_pipe_free(hid_class->intout);
167 }
168
169 if (hport->config.intf[intf].devname[0] != '\0') {
170 USB_LOG_INFO("Unregister HID Class:%s\r\n", hport->config.intf[intf].devname);
171 usbh_hid_stop(hid_class);
172 }
173
174 usbh_hid_class_free(hid_class);
175 }
176
177 return ret;
178 }
179
usbh_hid_run(struct usbh_hid * hid_class)180 __WEAK void usbh_hid_run(struct usbh_hid *hid_class)
181 {
182 }
183
usbh_hid_stop(struct usbh_hid * hid_class)184 __WEAK void usbh_hid_stop(struct usbh_hid *hid_class)
185 {
186 }
187
188 const struct usbh_class_driver hid_class_driver = {
189 .driver_name = "hid",
190 .connect = usbh_hid_connect,
191 .disconnect = usbh_hid_disconnect
192 };
193
194 CLASS_INFO_DEFINE const struct usbh_class_info hid_custom_class_info = {
195 .match_flags = USB_CLASS_MATCH_INTF_CLASS,
196 .class = USB_DEVICE_CLASS_HID,
197 .subclass = 0x00,
198 .protocol = 0x00,
199 .vid = 0x00,
200 .pid = 0x00,
201 .class_driver = &hid_class_driver
202 };
203