1 /*
2 * Copyright (c) 2006-2023, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2011-12-12 Yi Qiu first version
9 */
10
11 #include <rtthread.h>
12 #include <drivers/usb_host.h>
13 #include "adk.h"
14
15 #ifdef RT_USBH_ADK
16
17 #define DBG_TAG "usbhost.adk"
18 #define DBG_LVL DBG_INFO
19 #include <rtdbg.h>
20
21 static struct uclass_driver adk_driver;
22 static const char* _adk_manufacturer = RT_NULL;
23 static const char* _adk_model = RT_NULL;
24 static const char* _adk_description = RT_NULL;
25 static const char* _adk_version = RT_NULL;
26 static const char* _adk_uri = RT_NULL;
27 static const char* _adk_serial = RT_NULL;
28
rt_usbh_adk_set_string(const char * manufacturer,const char * model,const char * description,const char * _version,const char * uri,const char * serial)29 rt_err_t rt_usbh_adk_set_string(const char* manufacturer, const char* model,
30 const char* description, const char* _version, const char* uri,
31 const char* serial)
32 {
33 _adk_manufacturer = manufacturer;
34 _adk_model = model;
35 _adk_description = description;
36 _adk_version = _version;
37 _adk_uri = uri;
38 _adk_serial = serial;
39
40 return RT_EOK;
41 }
42
43 #ifdef RT_USING_MODULE
44 #include <rtm.h>
45
46 RTM_EXPORT(rt_usbh_adk_set_string);
47 #endif
48
49 /**
50 * This function will do USB_REQ_GET_PROTOCOL request to set idle period to the usb adk device
51 *
52 * @param intf the interface instance.
53 * @duration the idle period of requesting data.
54 * @report_id the report id
55 *
56 * @return the error code, RT_EOK on successfully.
57 */
rt_usbh_adk_get_protocol(struct uintf * intf,rt_uint16_t * protocol)58 static rt_err_t rt_usbh_adk_get_protocol(struct uintf* intf, rt_uint16_t *protocol)
59 {
60 struct urequest setup;
61 uinst_t device;
62 int timeout = USB_TIMEOUT_BASIC;
63
64 /* parameter check */
65 RT_ASSERT(intf != RT_NULL);
66 RT_ASSERT(intf->device != RT_NULL);
67
68 device = intf->device;
69
70 setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_VENDOR |
71 USB_REQ_TYPE_DEVICE;
72 setup.request = USB_REQ_GET_PROTOCOL;
73 setup.index = 0;
74 setup.length = 2;
75 setup.value = 0;
76
77 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, (void*)protocol, 2,
78 timeout) == 0) return RT_EOK;
79 else return -RT_FALSE;
80 }
81
82 /**
83 * This function will do USB_REQ_SEND_STRING request to set idle period to the usb adk device
84 *
85 * @param intf the interface instance.
86 * @duration the idle period of requesting data.
87 * @report_id the report id
88 *
89 * @return the error code, RT_EOK on successfully.
90 */
rt_usbh_adk_send_string(struct uintf * intf,rt_uint16_t index,const char * str)91 static rt_err_t rt_usbh_adk_send_string(struct uintf* intf, rt_uint16_t index,
92 const char* str)
93 {
94 struct urequest setup;
95 uinst_t device;
96 int timeout = USB_TIMEOUT_BASIC;
97
98 /* parameter check */
99 RT_ASSERT(intf != RT_NULL);
100 RT_ASSERT(intf->device != RT_NULL);
101
102 device = intf->device;
103
104 setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_VENDOR |
105 USB_REQ_TYPE_DEVICE;
106 setup.request = USB_REQ_SEND_STRING;
107 setup.index = index;
108 setup.length = rt_strlen(str) + 1;
109 setup.value = 0;
110
111 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, (void*)str,
112 rt_strlen(str) + 1, timeout) == 0) return RT_EOK;
113 else return -RT_FALSE;
114 }
115
116 /**
117 * This function will do USB_REQ_START request to set idle period to the usb adk device
118 *
119 * @param intf the interface instance.
120 * @duration the idle period of requesting data.
121 * @report_id the report id
122 *
123 * @return the error code, RT_EOK on successfully.
124 */
rt_usbh_adk_start(struct uintf * intf)125 static rt_err_t rt_usbh_adk_start(struct uintf* intf)
126 {
127 struct urequest setup;
128 uinst_t device;
129 int timeout = USB_TIMEOUT_BASIC;
130
131 /* parameter check */
132 RT_ASSERT(intf != RT_NULL);
133 RT_ASSERT(intf->device != RT_NULL);
134
135 device = intf->device;
136
137 setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_VENDOR |
138 USB_REQ_TYPE_DEVICE;
139 setup.request = USB_REQ_START;
140 setup.index = 0;
141 setup.length = 0;
142 setup.value = 0;
143
144 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, RT_NULL, 0,
145 timeout) == 0) return RT_EOK;
146 else return -RT_FALSE;
147 }
148
149 /**
150 * This function will read data from usb adk device
151 *
152 * @param intf the interface instance.
153 *
154 * @return the error code, RT_EOK on successfully.
155 */
rt_usbh_adk_read(rt_device_t device,rt_off_t pos,void * buffer,rt_size_t size)156 static rt_ssize_t rt_usbh_adk_read(rt_device_t device, rt_off_t pos, void* buffer,
157 rt_size_t size)
158 {
159 uadk_t adk;
160 rt_size_t length;
161 struct uintf* intf;
162
163 /* check parameter */
164 RT_ASSERT(device != RT_NULL);
165 RT_ASSERT(buffer != RT_NULL);
166
167 intf = (struct uintf*)device->user_data;
168 adk = (uadk_t)intf->user_data;
169
170 length = rt_usb_hcd_bulk_xfer(intf->device->hcd, adk->pipe_in,
171 buffer, size, 300);
172
173 return length;
174
175 }
176
177 /**
178 * This function will write data to usb adk device
179 *
180 * @param intf the interface instance.
181 *
182 * @return the error code, RT_EOK on successfully.
183 */
rt_usbh_adk_write(rt_device_t device,rt_off_t pos,const void * buffer,rt_size_t size)184 static rt_ssize_t rt_usbh_adk_write (rt_device_t device, rt_off_t pos, const void* buffer,
185 rt_size_t size)
186 {
187 uadk_t adk;
188 rt_size_t length;
189 struct uintf* intf;
190
191 RT_ASSERT(buffer != RT_NULL);
192
193 intf = (struct uintf*)device->user_data;
194 adk = (uadk_t)intf->user_data;
195
196 length = rt_usb_hcd_bulk_xfer(intf->device->hcd, adk->pipe_out,
197 (void*)buffer, size, 300);
198
199 return length;
200 }
201
202 #ifdef RT_USING_DEVICE_OPS
203 const static struct rt_device_ops adk_device_ops =
204 {
205 RT_NULL;
206 RT_NULL;
207 RT_NULL;
208 rt_usbh_adk_read;
209 rt_usbh_adk_write;
210 RT_NULL;
211 };
212 #endif
213
214 /**
215 * This function will run adk class driver when usb device is detected and identified
216 * as a adk class device, it will continue the enumulate process.
217 *
218 * @param arg the argument.
219 *
220 * @return the error code, RT_EOK on successfully.
221 */
rt_usbh_adk_enable(void * arg)222 static rt_err_t rt_usbh_adk_enable(void* arg)
223 {
224 int i = 0;
225 uadk_t adk;
226 struct uintf* intf = (struct uintf*)arg;
227 udev_desc_t dev_desc;
228 rt_uint16_t protocol;
229 rt_err_t ret;
230
231 /* parameter check */
232 if(intf == RT_NULL)
233 {
234 rt_kprintf("the interface is not available\n");
235 return -RT_EIO;
236 }
237
238 LOG_D("rt_usbh_adk_run");
239
240 dev_desc = &intf->device->dev_desc;
241 if(dev_desc->idVendor == USB_ACCESSORY_VENDOR_ID &&
242 (dev_desc->idProduct == USB_ACCESSORY_PRODUCT_ID ||
243 dev_desc->idProduct == USB_ACCESSORY_ADB_PRODUCT_ID))
244 {
245 if(intf->intf_desc->bInterfaceSubClass != 0xFF) return -RT_ERROR;
246
247 LOG_D("found android accessory device");
248 }
249 else
250 {
251 LOG_D("switch device");
252
253 if((ret = rt_usbh_adk_get_protocol(intf, &protocol)) != RT_EOK)
254 {
255 rt_kprintf("rt_usbh_adk_get_protocol failed\n");
256 return ret;
257 }
258
259 if(protocol != 1)
260 {
261 rt_kprintf("read protocol failed\n");
262 return -RT_ERROR;
263 }
264
265 rt_usbh_adk_send_string(intf,
266 ACCESSORY_STRING_MANUFACTURER, _adk_manufacturer);
267 rt_usbh_adk_send_string(intf,
268 ACCESSORY_STRING_MODEL, _adk_model);
269 rt_usbh_adk_send_string(intf,
270 ACCESSORY_STRING_DESCRIPTION, _adk_description);
271 rt_usbh_adk_send_string(intf,
272 ACCESSORY_STRING_VERSION, _adk_version);
273 rt_usbh_adk_send_string(intf,
274 ACCESSORY_STRING_URI, _adk_uri);
275 rt_usbh_adk_send_string(intf,
276 ACCESSORY_STRING_SERIAL, _adk_serial);
277
278 LOG_D("manufacturer %s", _adk_manufacturer);
279 LOG_D("model %s", _adk_model);
280 LOG_D("description %s", _adk_description);
281 LOG_D("version %s", _adk_version);
282 LOG_D("uri %s", _adk_uri);
283 LOG_D("serial %s", _adk_serial);
284
285 if((ret = rt_usbh_adk_start(intf)) != RT_EOK)
286 {
287 rt_kprintf("rt_usbh_adk_start failed\n");
288 return ret;
289 }
290
291 return RT_EOK;
292 }
293
294 adk = rt_malloc(sizeof(struct uadkinst));
295 RT_ASSERT(adk != RT_NULL);
296
297 /* initilize the data structure */
298 rt_memset(adk, 0, sizeof(struct uadkinst));
299 intf->user_data = (void*)adk;
300
301 for(i=0; i<intf->intf_desc->bNumEndpoints; i++)
302 {
303 uep_desc_t ep_desc;
304
305 /* get endpoint descriptor from interface descriptor */
306 rt_usbh_get_endpoint_descriptor(intf->intf_desc, i, &ep_desc);
307 if(ep_desc == RT_NULL)
308 {
309 rt_kprintf("rt_usb_get_endpoint_descriptor error\n");
310 return -RT_ERROR;
311 }
312
313 /* the endpoint type of adk class should be BULK */
314 if((ep_desc->bmAttributes & USB_EP_ATTR_TYPE_MASK) != USB_EP_ATTR_BULK)
315 continue;
316
317 /* allocate pipes according to the endpoint type */
318 if(ep_desc->bEndpointAddress & USB_DIR_IN)
319 {
320 /* allocate an in pipe for the adk instance */
321 ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &adk->pipe_in,
322 intf, ep_desc, RT_NULL);
323 if(ret != RT_EOK) return ret;
324 }
325 else
326 {
327 /* allocate an output pipe for the adk instance */
328 ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &adk->pipe_out,
329 intf, ep_desc, RT_NULL);
330 if(ret != RT_EOK) return ret;
331 }
332 }
333
334 /* check pipes infomation */
335 if(adk->pipe_in == RT_NULL || adk->pipe_out == RT_NULL)
336 {
337 rt_kprintf("pipe error, unsupported device\n");
338 return -RT_ERROR;
339 }
340
341 /* set configuration */
342 ret = rt_usbh_set_configure(intf->device, 1);
343 if(ret != RT_EOK) return ret;
344
345 /* register adk device */
346 adk->device.type = RT_Device_Class_Char;
347 #ifdef RT_USING_DEVICE_OPS
348 adk->device.ops = &adk_device_ops;
349 #else
350 adk->device.init = RT_NULL;
351 adk->device.open = RT_NULL;
352 adk->device.close = RT_NULL;
353 adk->device.read = rt_usbh_adk_read;
354 adk->device.write = rt_usbh_adk_write;
355 adk->device.control = RT_NULL;
356 #endif
357 adk->device.user_data = (void*)intf;
358
359 rt_device_register(&adk->device, "adkdev", RT_DEVICE_FLAG_RDWR);
360
361 return RT_EOK;
362 }
363
364 /**
365 * This function will be invoked when usb device plug out is detected and it would clean
366 * and release all hub class related resources.
367 *
368 * @param arg the argument.
369 *
370 * @return the error code, RT_EOK on successfully.
371 */
rt_usbh_adk_disable(void * arg)372 static rt_err_t rt_usbh_adk_disable(void* arg)
373 {
374 uadk_t adk;
375 struct uintf* intf = (struct uintf*)arg;
376
377 RT_ASSERT(intf != RT_NULL);
378
379 LOG_D("rt_usbh_adk_stop");
380
381 adk = (uadk_t)intf->user_data;
382 if(adk == RT_NULL)
383 {
384 rt_free(intf);
385 return RT_EOK;
386 }
387
388 if(adk->pipe_in != RT_NULL)
389 rt_usb_hcd_free_pipe(intf->device->hcd, adk->pipe_in);
390
391 if(adk->pipe_out != RT_NULL)
392 rt_usb_hcd_free_pipe(intf->device->hcd, adk->pipe_out);
393
394 /* unregister adk device */
395 rt_device_unregister(&adk->device);
396
397 /* free adk instance */
398 if(adk != RT_NULL)
399 {
400 rt_free(adk);
401 }
402
403 /* free interface instance */
404 rt_free(intf);
405
406 return RT_EOK;
407 }
408
409 /**
410 * This function will register adk class driver to the usb class driver manager.
411 * and it should be invoked in the usb system initialization.
412 *
413 * @return the error code, RT_EOK on successfully.
414 */
rt_usbh_class_driver_adk(void)415 ucd_t rt_usbh_class_driver_adk(void)
416 {
417 adk_driver.class_code = USB_CLASS_ADK;
418
419 adk_driver.enable = rt_usbh_adk_enable;
420 adk_driver.disable = rt_usbh_adk_disable;
421
422 return &adk_driver;
423 }
424
425 #endif
426
427