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 };