1 /*
2 * Copyright (c) 2006-2018, 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 "hid.h"
14
15 #ifdef RT_USBH_HID
16
17 static struct uclass_driver hid_driver;
18 static rt_list_t _protocal_list;
19
20 /**
21 * This function will do USB_REQ_SET_IDLE request to set idle period to the usb hid device
22 *
23 * @param intf the interface instance.
24 * @duration the idle period of requesting data.
25 * @report_id the report id
26 *
27 * @return the error code, RT_EOK on successfully.
28 */
rt_usbh_hid_set_idle(struct uintf * intf,int duration,int report_id)29 rt_err_t rt_usbh_hid_set_idle(struct uintf* intf, int duration, int report_id)
30 {
31 struct urequest setup;
32 struct uinstance* device;
33 int timeout = USB_TIMEOUT_BASIC;
34
35 /* parameter check */
36 RT_ASSERT(intf != RT_NULL);
37 RT_ASSERT(intf->device != RT_NULL);
38
39 device = intf->device;
40
41 setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS |
42 USB_REQ_TYPE_INTERFACE;
43 setup.request = USB_REQ_SET_IDLE;
44 setup.index = 0;
45 setup.length = 0;
46 setup.value = (duration << 8 )| report_id;
47
48 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, RT_NULL, 0,
49 timeout) == 0) return RT_EOK;
50 else return -RT_FALSE;
51 }
52
53 /**
54 * This function will do USB_REQ_GET_REPORT request to get report from the usb hid device
55 *
56 * @param intf the interface instance.
57 * @buffer the data buffer to save usb report descriptor.
58 * @param nbytes the size of buffer
59 *
60 * @return the error code, RT_EOK on successfully.
61 */
rt_usbh_hid_get_report(struct uintf * intf,rt_uint8_t type,rt_uint8_t id,rt_uint8_t * buffer,rt_size_t size)62 rt_err_t rt_usbh_hid_get_report(struct uintf* intf, rt_uint8_t type,
63 rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size)
64 {
65 struct urequest setup;
66 struct uinstance* device;
67 int timeout = USB_TIMEOUT_BASIC;
68
69 /* parameter check */
70 RT_ASSERT(intf != RT_NULL);
71 RT_ASSERT(intf->device != RT_NULL);
72
73 device = intf->device;
74
75 setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS |
76 USB_REQ_TYPE_INTERFACE;
77 setup.request = USB_REQ_GET_REPORT;
78 setup.index = intf->intf_desc->bInterfaceNumber;
79 setup.length = size;
80 setup.value = (type << 8 ) + id;
81
82 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, buffer, size,
83 timeout) == size) return RT_EOK;
84 else return -RT_FALSE;
85 }
86
87 /**
88 * This function will do USB_REQ_SET_REPORT request to set report to the usb hid device
89 *
90 * @param intf the interface instance.
91 * @buffer the data buffer to save usb report descriptor.
92 * @param nbytes the size of buffer
93 *
94 * @return the error code, RT_EOK on successfully.
95 */
rt_usbh_hid_set_report(struct uintf * intf,rt_uint8_t * buffer,rt_size_t size)96 rt_err_t rt_usbh_hid_set_report(struct uintf* intf, rt_uint8_t *buffer, rt_size_t size)
97 {
98 struct urequest setup;
99 struct uinstance* device;
100 int timeout = USB_TIMEOUT_BASIC;
101
102 /* parameter check */
103 RT_ASSERT(intf != RT_NULL);
104 RT_ASSERT(intf->device != RT_NULL);
105
106 device = intf->device;
107
108 setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS |
109 USB_REQ_TYPE_INTERFACE;
110 setup.request = USB_REQ_SET_REPORT;
111 setup.index = intf->intf_desc->bInterfaceNumber;
112 setup.length = size;
113 setup.value = 0x02 << 8;
114
115 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, buffer, size,
116 timeout) == size) return RT_EOK;
117 else return -RT_FALSE;
118 }
119
120 /**
121 * This function will do USB_REQ_SET_PROTOCOL request to set protocal to the usb hid device.
122 *
123 * @param intf the interface instance.
124 * @param protocol the protocol id.
125 *
126 * @return the error code, RT_EOK on successfully.
127 */
rt_usbh_hid_set_protocal(struct uintf * intf,int protocol)128 rt_err_t rt_usbh_hid_set_protocal(struct uintf* intf, int protocol)
129 {
130 struct urequest setup;
131 struct uinstance* device;
132 int timeout = USB_TIMEOUT_BASIC;
133
134 /* parameter check */
135 RT_ASSERT(intf != RT_NULL);
136 RT_ASSERT(intf->device != RT_NULL);
137
138 device = intf->device;
139
140 setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS |
141 USB_REQ_TYPE_INTERFACE;
142 setup.request = USB_REQ_SET_PROTOCOL;
143 setup.index = 0;
144 setup.length = 0;
145 setup.value = protocol;
146
147 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, RT_NULL, 0,
148 timeout) == 0) return RT_EOK;
149 else return -RT_FALSE;
150 }
151
152 /**
153 * This function will do USB_REQ_GET_DESCRIPTOR request for the device instance
154 * to set feature of the hub port.
155 *
156 * @param intf the interface instance.
157 * @buffer the data buffer to save usb report descriptor.
158 * @param nbytes the size of buffer
159 *
160 * @return the error code, RT_EOK on successfully.
161 */
rt_usbh_hid_get_report_descriptor(struct uintf * intf,rt_uint8_t * buffer,rt_size_t size)162 rt_err_t rt_usbh_hid_get_report_descriptor(struct uintf* intf,
163 rt_uint8_t *buffer, rt_size_t size)
164 {
165 struct urequest setup;
166 struct uinstance* device;
167 int timeout = USB_TIMEOUT_BASIC;
168
169 /* parameter check */
170 RT_ASSERT(intf != RT_NULL);
171 RT_ASSERT(intf->device != RT_NULL);
172
173 device = intf->device;
174
175 setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_STANDARD|
176 USB_REQ_TYPE_INTERFACE;
177 setup.request = USB_REQ_GET_DESCRIPTOR;
178 setup.index = 0;
179 setup.length = size;
180 setup.value = USB_DESC_TYPE_REPORT << 8;
181
182 if(rt_usb_hcd_control_xfer(device->hcd, device, &setup, buffer, size,
183 timeout) == size) return RT_EOK;
184 else return -RT_FALSE;
185 }
186
187 /**
188 * This function will register specified hid protocal to protocal list
189 *
190 * @param protocal the specified protocal.
191 *
192 * @return the error code, RT_EOK on successfully.
193 */
rt_usbh_hid_protocal_register(uprotocal_t protocal)194 rt_err_t rt_usbh_hid_protocal_register(uprotocal_t protocal)
195 {
196 RT_ASSERT(protocal != RT_NULL);
197
198 if (protocal == RT_NULL) return -RT_ERROR;
199
200 /* insert class driver into driver list */
201 rt_list_insert_after(&_protocal_list, &(protocal->list));
202
203 return RT_EOK;
204 }
205
206 /**
207 * This function is the callback function of hid's int endpoint, it is invoked when data comes.
208 *
209 * @param context the context of the callback function.
210 *
211 * @return none.
212 */
rt_usbh_hid_callback(void * context)213 static void rt_usbh_hid_callback(void* context)
214 {
215 upipe_t pipe;
216 struct uhid* hid;
217 int timeout = USB_TIMEOUT_LONG;
218
219 /* parameter check */
220 RT_ASSERT(context != RT_NULL);
221
222 pipe = (upipe_t)context;
223 hid = (struct uhid*)pipe->intf->user_data;
224
225 /* invoke protocal callback function */
226 hid->protocal->callback((void*)hid);
227
228 /* parameter check */
229 RT_ASSERT(pipe->intf->device->hcd != RT_NULL);
230
231 rt_usb_hcd_int_xfer(pipe->intf->device->hcd, pipe, hid->buffer,
232 pipe->ep.wMaxPacketSize, timeout);
233 }
234
235 /**
236 * This function will find specified hid protocal from protocal list
237 *
238 * @param pro_id the protocal id.
239 *
240 * @return the found protocal or RT_NULL if there is no this protocal.
241 */
rt_usbh_hid_protocal_find(int pro_id)242 static uprotocal_t rt_usbh_hid_protocal_find(int pro_id)
243 {
244 struct rt_list_node *node;
245
246 /* try to find protocal object */
247 for (node = _protocal_list.next; node != &_protocal_list; node = node->next)
248 {
249 uprotocal_t protocal =
250 (uprotocal_t)rt_list_entry(node, struct uprotocal, list);
251 if (protocal->pro_id == pro_id) return protocal;
252 }
253
254 /* not found */
255 return RT_NULL;
256 }
257
258 /**
259 * This function will run hid class driver when usb device is detected and identified
260 * as a hid class device, it will continue the enumulate process.
261 *
262 * @param arg the argument.
263 *
264 * @return the error code, RT_EOK on successfully.
265 */
rt_usbh_hid_enable(void * arg)266 static rt_err_t rt_usbh_hid_enable(void* arg)
267 {
268 int i = 0, pro_id;
269 uprotocal_t protocal;
270 struct uhid* hid;
271 struct uintf* intf = (struct uintf*)arg;
272 int timeout = USB_TIMEOUT_BASIC;
273 upipe_t pipe;
274
275 /* parameter check */
276 if(intf == RT_NULL)
277 {
278 rt_kprintf("the interface is not available\n");
279 return -RT_EIO;
280 }
281
282 pro_id = intf->intf_desc->bInterfaceProtocol;
283
284 RT_DEBUG_LOG(RT_DEBUG_USB,
285 ("HID device enable, protocal id %d\n", pro_id));
286
287 protocal = rt_usbh_hid_protocal_find(pro_id);
288 if(protocal == RT_NULL)
289 {
290 rt_kprintf("can't find hid protocal %d\n", pro_id);
291 intf->user_data = RT_NULL;
292 return -RT_ERROR;
293 }
294
295 hid = rt_malloc(sizeof(struct uhid));
296 RT_ASSERT(hid != RT_NULL);
297
298 /* initilize the data structure */
299 rt_memset(hid, 0, sizeof(struct uhid));
300 intf->user_data = (void*)hid;
301 hid->protocal = protocal;
302
303 for(i=0; i<intf->intf_desc->bNumEndpoints; i++)
304 {
305 rt_err_t ret;
306 uep_desc_t ep_desc;
307
308 /* get endpoint descriptor */
309 rt_usbh_get_endpoint_descriptor(intf->intf_desc, i, &ep_desc);
310 if(ep_desc == RT_NULL)
311 {
312 rt_kprintf("rt_usbh_get_endpoint_descriptor error\n");
313 return -RT_ERROR;
314 }
315
316 if(USB_EP_ATTR(ep_desc->bmAttributes) != USB_EP_ATTR_INT)
317 continue;
318
319 if(!(ep_desc->bEndpointAddress & USB_DIR_IN)) continue;
320
321 ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &hid->pipe_in,
322 intf, ep_desc, rt_usbh_hid_callback);
323 if(ret != RT_EOK) return ret;
324 }
325
326 /* initialize hid protocal */
327 hid->protocal->init((void*)intf);
328 pipe = hid->pipe_in;
329
330 /* parameter check */
331 RT_ASSERT(pipe->intf->device->hcd != RT_NULL);
332
333 rt_usb_hcd_int_xfer(pipe->intf->device->hcd, hid->pipe_in,
334 hid->buffer, hid->pipe_in->ep.wMaxPacketSize, timeout);
335 return RT_EOK;
336 }
337
338 /**
339 * This function will be invoked when usb device plug out is detected and it would clean
340 * and release all hub class related resources.
341 *
342 * @param arg the argument.
343 *
344 * @return the error code, RT_EOK on successfully.
345 */
rt_usbh_hid_disable(void * arg)346 static rt_err_t rt_usbh_hid_disable(void* arg)
347 {
348 struct uhid* hid;
349 struct uintf* intf = (struct uintf*)arg;
350
351 RT_ASSERT(intf != RT_NULL);
352
353 RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_hid_disable\n"));
354
355 hid = (struct uhid*)intf->user_data;
356 if(hid != RT_NULL)
357 {
358 if(hid->pipe_in != RT_NULL)
359 {
360 /* free the HID in pipe */
361 rt_usb_hcd_free_pipe(intf->device->hcd, hid->pipe_in);
362 }
363
364 /* free the hid instance */
365 rt_free(hid);
366 }
367
368 /* free the instance */
369 rt_free(intf);
370
371 return RT_EOK;
372 }
373
374 /**
375 * This function will register hid class driver to the usb class driver manager.
376 * and it should be invoked in the usb system initialization.
377 *
378 * @return the error code, RT_EOK on successfully.
379 */
rt_usbh_class_driver_hid(void)380 ucd_t rt_usbh_class_driver_hid(void)
381 {
382 rt_list_init(&_protocal_list);
383
384 hid_driver.class_code = USB_CLASS_HID;
385
386 hid_driver.enable = rt_usbh_hid_enable;
387 hid_driver.disable = rt_usbh_hid_disable;
388
389 return &hid_driver;
390 }
391
392 #endif
393
394