1 /*
2 * Copyright (c) 2024, sakumisu
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include "usbh_core.h"
7 #include "usbh_aoa.h"
8
9 #undef USB_DBG_TAG
10 #define USB_DBG_TAG "usbh_aoa"
11 #include "usb_log.h"
12
13 #define DEV_FORMAT "/dev/aoa"
14
15 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_aoa_buffer[USB_ALIGN_UP(128, CONFIG_USB_ALIGN_SIZE)];
16
17 static struct usbh_aoa g_aoa_class;
18
usbh_aoa_switch(struct usbh_hubport * hport,struct aoa_string_info * info)19 int usbh_aoa_switch(struct usbh_hubport *hport, struct aoa_string_info *info)
20 {
21 struct usb_setup_packet *setup;
22 int ret;
23
24 setup = hport->setup;
25
26 if (setup == NULL) {
27 return -USB_ERR_INVAL;
28 }
29
30 USB_LOG_INFO("Try switch into aoa mode\r\n");
31
32 setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
33 setup->bRequest = AOA_ACCESSORY_GET_PROTOCOL;
34 setup->wValue = 0;
35 setup->wIndex = 0;
36 setup->wLength = 2;
37
38 ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
39 if (ret < 0) {
40 return ret;
41 }
42
43 USB_LOG_INFO("AOA version: v%d.%d\r\n", g_aoa_buffer[0], g_aoa_buffer[1]);
44
45 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
46 setup->bRequest = AOA_ACCESSORY_SEND_STRING;
47 setup->wValue = 0;
48 setup->wIndex = AOA_ACCESSORY_STRING_MANUFACTURER;
49 setup->wLength = strlen(info->acc_manufacturer) + 1;
50
51 memcpy(g_aoa_buffer, info->acc_manufacturer, strlen(info->acc_manufacturer));
52 ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
53 if (ret < 0) {
54 return ret;
55 }
56
57 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
58 setup->bRequest = AOA_ACCESSORY_SEND_STRING;
59 setup->wValue = 0;
60 setup->wIndex = AOA_ACCESSORY_STRING_MODEL;
61 setup->wLength = strlen(info->acc_model) + 1;
62
63 memcpy(g_aoa_buffer, info->acc_model, strlen(info->acc_model));
64 ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
65 if (ret < 0) {
66 return ret;
67 }
68
69 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
70 setup->bRequest = AOA_ACCESSORY_SEND_STRING;
71 setup->wValue = 0;
72 setup->wIndex = AOA_ACCESSORY_STRING_DESCRIPTION;
73 setup->wLength = strlen(info->acc_description) + 1;
74
75 memcpy(g_aoa_buffer, info->acc_description, strlen(info->acc_description));
76 ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
77 if (ret < 0) {
78 return ret;
79 }
80
81 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
82 setup->bRequest = AOA_ACCESSORY_SEND_STRING;
83 setup->wValue = 0;
84 setup->wIndex = AOA_ACCESSORY_STRING_VERSION;
85 setup->wLength = strlen(info->acc_version) + 1;
86
87 memcpy(g_aoa_buffer, info->acc_version, strlen(info->acc_version));
88 ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
89 if (ret < 0) {
90 return ret;
91 }
92
93 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
94 setup->bRequest = AOA_ACCESSORY_SEND_STRING;
95 setup->wValue = 0;
96 setup->wIndex = AOA_ACCESSORY_STRING_URI;
97 setup->wLength = strlen(info->acc_uri) + 1;
98
99 memcpy(g_aoa_buffer, info->acc_uri, strlen(info->acc_uri));
100 ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
101 if (ret < 0) {
102 return ret;
103 }
104
105 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
106 setup->bRequest = AOA_ACCESSORY_SEND_STRING;
107 setup->wValue = 0;
108 setup->wIndex = AOA_ACCESSORY_STRING_SERIAL;
109 setup->wLength = strlen(info->acc_serial) + 1;
110
111 memcpy(g_aoa_buffer, info->acc_serial, strlen(info->acc_serial));
112 ret = usbh_control_transfer(hport, setup, g_aoa_buffer);
113 if (ret < 0) {
114 return ret;
115 }
116
117 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
118 setup->bRequest = AOA_ACCESSORY_START;
119 setup->wValue = 0;
120 setup->wIndex = 0;
121 setup->wLength = 0;
122
123 ret = usbh_control_transfer(hport, setup, NULL);
124 if (ret < 0) {
125 return ret;
126 }
127
128 USB_LOG_INFO("Switch into aoa mode success, wait usb device restart...\r\n");
129 return 0;
130 }
131
usbh_aoa_register_hid(struct usbh_aoa * aoa_class,uint16_t id,uint8_t * report,uint32_t report_len)132 int usbh_aoa_register_hid(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *report, uint32_t report_len)
133 {
134 struct usb_setup_packet *setup;
135 int ret;
136 uint8_t len;
137 uint32_t offset;
138
139 if (!aoa_class || !aoa_class->hport) {
140 return -USB_ERR_INVAL;
141 }
142 setup = aoa_class->hport->setup;
143
144 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
145 setup->bRequest = AOA_ACCESSORY_REGISTER_HID;
146 setup->wValue = id;
147 setup->wIndex = report_len;
148 setup->wLength = 0;
149
150 ret = usbh_control_transfer(aoa_class->hport, setup, NULL);
151 if (ret < 0) {
152 return ret;
153 }
154
155 offset = 0;
156 while (report_len > 0) {
157 len = report_len > 64 ? 64 : report_len;
158
159 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
160 setup->bRequest = AOA_ACCESSORY_SET_HID_REPORT_DESC;
161 setup->wValue = id;
162 setup->wIndex = offset;
163 setup->wLength = len;
164
165 memcpy(g_aoa_buffer, report + offset, len);
166 ret = usbh_control_transfer(aoa_class->hport, setup, g_aoa_buffer);
167 if (ret < 0) {
168 return ret;
169 }
170 offset += len;
171 report_len -= len;
172 }
173 return ret;
174 }
175
usbh_aoa_send_hid_event(struct usbh_aoa * aoa_class,uint16_t id,uint8_t * event,uint32_t event_len)176 int usbh_aoa_send_hid_event(struct usbh_aoa *aoa_class, uint16_t id, uint8_t *event, uint32_t event_len)
177 {
178 struct usb_setup_packet *setup;
179 int ret;
180 uint8_t len;
181 uint32_t offset;
182
183 if (!aoa_class || !aoa_class->hport) {
184 return -USB_ERR_INVAL;
185 }
186 setup = aoa_class->hport->setup;
187
188 setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_VENDOR | USB_REQUEST_RECIPIENT_DEVICE;
189 setup->bRequest = AOA_ACCESSORY_SEND_HID_EVENT;
190 setup->wValue = id;
191 setup->wIndex = 0;
192 setup->wLength = event_len;
193
194 memcpy(g_aoa_buffer, event, event_len);
195 return usbh_control_transfer(aoa_class->hport, setup, event);
196 }
197
usbh_aoa_connect(struct usbh_hubport * hport,uint8_t intf)198 static int usbh_aoa_connect(struct usbh_hubport *hport, uint8_t intf)
199 {
200 struct usb_endpoint_descriptor *ep_desc;
201 int ret = 0;
202
203 struct usbh_aoa *aoa_class = &g_aoa_class;
204
205 memset(aoa_class, 0, sizeof(struct usbh_aoa));
206
207 aoa_class->hport = hport;
208 aoa_class->intf = intf;
209
210 hport->config.intf[intf].priv = aoa_class;
211
212 for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
213 ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
214
215 if (ep_desc->bEndpointAddress & 0x80) {
216 USBH_EP_INIT(aoa_class->bulkin, ep_desc);
217 } else {
218 USBH_EP_INIT(aoa_class->bulkout, ep_desc);
219 }
220 }
221
222 strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
223
224 USB_LOG_INFO("Register AOA Class:%s\r\n", hport->config.intf[intf].devname);
225
226 usbh_aoa_run(aoa_class);
227 return 0;
228 }
229
usbh_aoa_disconnect(struct usbh_hubport * hport,uint8_t intf)230 static int usbh_aoa_disconnect(struct usbh_hubport *hport, uint8_t intf)
231 {
232 int ret = 0;
233
234 struct usbh_aoa *aoa_class = (struct usbh_aoa *)hport->config.intf[intf].priv;
235
236 if (aoa_class) {
237 if (aoa_class->bulkin) {
238 usbh_kill_urb(&aoa_class->bulkin_urb);
239 }
240
241 if (aoa_class->bulkout) {
242 usbh_kill_urb(&aoa_class->bulkout_urb);
243 }
244
245 if (hport->config.intf[intf].devname[0] != '\0') {
246 USB_LOG_INFO("Unregister AOA Class:%s\r\n", hport->config.intf[intf].devname);
247 usbh_aoa_stop(aoa_class);
248 }
249
250 memset(aoa_class, 0, sizeof(struct usbh_aoa));
251 }
252
253 return ret;
254 }
255
usbh_aoa_run(struct usbh_aoa * aoa_class)256 __WEAK void usbh_aoa_run(struct usbh_aoa *aoa_class)
257 {
258 (void)aoa_class;
259 }
260
usbh_aoa_stop(struct usbh_aoa * aoa_class)261 __WEAK void usbh_aoa_stop(struct usbh_aoa *aoa_class)
262 {
263 (void)aoa_class;
264 }
265
266 static const uint16_t aoa_id_table[][2] = {
267 { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_PRODUCT_ID },
268 { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_ADB_PRODUCT_ID },
269 { AOA_ACCESSORY_VENDOR_ID, AOA_AUDIO_PRODUCT_ID },
270 { AOA_ACCESSORY_VENDOR_ID, AOA_AUDIO_ADB_PRODUCT_ID },
271 { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_AUDIO_PRODUCT_ID },
272 { AOA_ACCESSORY_VENDOR_ID, AOA_ACCESSORY_AUDIO_ADB_PRODUCT_ID },
273 { 0, 0 },
274 };
275
276 const struct usbh_class_driver aoa_class_driver = {
277 .driver_name = "aoa",
278 .connect = usbh_aoa_connect,
279 .disconnect = usbh_aoa_disconnect
280 };
281
282 CLASS_INFO_DEFINE const struct usbh_class_info aoa_intf_class_info = {
283 .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS | USB_CLASS_MATCH_INTF_SUBCLASS,
284 .bInterfaceClass = 0xff,
285 .bInterfaceSubClass = 0xff,
286 .bInterfaceProtocol = 0x00,
287 .id_table = aoa_id_table,
288 .class_driver = &aoa_class_driver
289 };