xref: /nrf52832-nimble/rt-thread/components/drivers/usb/usbhost/class/hid.c (revision 167494296f0543431a51b6b1b83e957045294e05)
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 */
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 */
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 */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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