由于我自己需要用到USBhost接口访问HID设备,所以掉进HID坑里了,官网的hid.c文件估
计有快10年没更新了,与现有代码存在不兼容和错误,然后把这些坑填了填,希望能帮助使
用HID时遇到问题的同学,能将host的hid用起来.
HID设备使用标准的HID协议,用户需要关心的主要是下面这两个接口:
rt_err_t rt_usbh_hid_set_report(struct uhintf* intf, rt_uint8_t type, rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size);
rt_err_t rt_usbh_hid_get_report(struct uhintf* intf, rt_uint8_t type, rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size);
rt_usbh_hid_set_report()接口用于向HID设备发送一条命令,
rt_usbh_hid_get_report()接口用于从HID设备读取返回数据,
所以在应用中这两个函数应该配合使用,如果没有返回数据,则只需set_report接口.
发送指令的格式为:
1) 先发送控制字协议包;对应rt_usb_hcd_setup_xfer()接口;
2) 接着发送用户命令包;对应rt_usb_hcd_pipe_xfer()接口;
3) rt_usb_hcd_pipe_xfer()接口如果传入的是pipe_ep0_out,就是向设备写数据;
rt_usb_hcd_pipe_xfer()接口如果传入的是pipe_ep0_in,就是从设备读数据;
总体来说rtthread的usb驱动架构,使用了线程异步处理,用户应用中数据流就类似于线性的,处理数据很方便;作为对比原子的usb驱动接口使用的状态机模式,在操作系统下,处理usb数据就很麻烦,反而非RTOS下处理起来更容易.
下面贴出hid源码,我们可以一起完善它
hid.c
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2011-12-12 Yi Qiu first version
*/
#include <rtthread.h>
#include <drivers/usb_host.h>
#include "hid.h"
#ifdef RT_USBH_HID
static struct uclass_driver hid_driver;
static rt_list_t _protocal_list;
/**
* This function will do USB_REQ_SET_IDLE request to set idle period to the usb hid device
*
* @param intf the interface instance.
* @duration the idle period of requesting data.
* @report_id the report id
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_usbh_hid_set_idle(struct uhintf* intf, int duration, int report_id)
{
struct urequest setup;
struct uinstance* device;
int timeout = USB_TIMEOUT_BASIC;
/* parameter check */
RT_ASSERT(intf != RT_NULL);
RT_ASSERT(intf->device != RT_NULL);
device = intf->device;
setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS |
USB_REQ_TYPE_INTERFACE;
setup.bRequest = USB_REQ_SET_IDLE;
setup.wIndex = 0;
setup.wLength = 0;
setup.wValue = (duration << 8 )| report_id;
if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup,
timeout) == 0) return RT_EOK;
else return -RT_ERROR;
}
/**
* This function will do USB_REQ_GET_REPORT request to get report from the usb hid device
*
* @param intf the interface instance.
* @buffer the data buffer to save usb report descriptor.
* @param nbytes the size of buffer
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_usbh_hid_get_report(struct uhintf* intf, rt_uint8_t type, rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size)
{
struct urequest setup;
struct uinstance* device;
int timeout = USB_TIMEOUT_BASIC;
/* parameter check */
RT_ASSERT(intf != RT_NULL);
RT_ASSERT(intf->device != RT_NULL);
device = intf->device;
setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE; //0xA1
setup.bRequest = USB_REQ_GET_REPORT; //0x01
setup.wIndex = intf->intf_desc->bInterfaceNumber;
setup.wLength = size;
setup.wValue = (type << 8 ) + id;
if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) {
return -RT_ERROR;
}
if (rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_in, buffer, size, timeout) != size) {
return -RT_ERROR;
}
return RT_EOK;
}
/**
* This function will do USB_REQ_SET_REPORT request to set report to the usb hid device
*
* @param intf the interface instance.
* @buffer the data buffer to save usb report descriptor.
* @param nbytes the size of buffer
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_usbh_hid_set_report(struct uhintf* intf, rt_uint8_t type, rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size)
{
struct urequest setup;
struct uinstance* device;
int timeout = USB_TIMEOUT_BASIC;
/* parameter check */
RT_ASSERT(intf != RT_NULL);
RT_ASSERT(intf->device != RT_NULL);
device = intf->device;
setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE; //0x21
setup.bRequest = USB_REQ_SET_REPORT; //0x09
setup.wValue = type << 8 || id;
setup.wIndex = intf->intf_desc->bInterfaceNumber;
setup.wLength = size;
if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup, timeout) != 8) {
return -RT_ERROR;
}
if (rt_usb_hcd_pipe_xfer(device->hcd, device->pipe_ep0_out, buffer, size, timeout) != size) {
return -RT_ERROR;
}
return RT_EOK;
}
/**
* This function will do USB_REQ_SET_PROTOCOL request to set protocal to the usb hid device.
*
* @param intf the interface instance.
* @param protocol the protocol id.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_usbh_hid_set_protocal(struct uhintf* intf, int protocol)
{
struct urequest setup;
struct uinstance* device;
int timeout = USB_TIMEOUT_BASIC;
/* parameter check */
RT_ASSERT(intf != RT_NULL);
RT_ASSERT(intf->device != RT_NULL);
device = intf->device;
setup.request_type = USB_REQ_TYPE_DIR_OUT | USB_REQ_TYPE_CLASS |
USB_REQ_TYPE_INTERFACE;
setup.bRequest = USB_REQ_SET_PROTOCOL;
setup.wIndex = 0;
setup.wLength = 0;
setup.wValue = protocol;
if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup,
timeout) == 0) return RT_EOK;
else return -RT_ERROR;
}
/**
* This function will do USB_REQ_GET_DESCRIPTOR request for the device instance
* to set feature of the hub port.
*
* @param intf the interface instance.
* @buffer the data buffer to save usb report descriptor.
* @param nbytes the size of buffer
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_usbh_hid_get_report_descriptor(struct uhintf* intf, rt_uint8_t *buffer, rt_size_t size)
{
struct urequest setup;
struct uinstance* device;
int timeout = USB_TIMEOUT_BASIC;
/* parameter check */
RT_ASSERT(intf != RT_NULL);
RT_ASSERT(intf->device != RT_NULL);
device = intf->device;
setup.request_type = USB_REQ_TYPE_DIR_IN | USB_REQ_TYPE_STANDARD|
USB_REQ_TYPE_INTERFACE;
setup.bRequest = USB_REQ_GET_DESCRIPTOR;
setup.wIndex = 0;
setup.wLength = size;
setup.wValue = USB_DESC_TYPE_REPORT << 8;
if(rt_usb_hcd_setup_xfer(device->hcd, device->pipe_ep0_out, &setup,
timeout) == size) return RT_EOK;
else return -RT_ERROR;
}
/**
* This function will register specified hid protocal to protocal list
*
* @param protocal the specified protocal.
*
* @return the error code, RT_EOK on successfully.
*/
rt_err_t rt_usbh_hid_protocal_register(uprotocal_t protocal)
{
RT_ASSERT(protocal != RT_NULL);
if (protocal == RT_NULL) return -RT_ERROR;
/* insert class driver into driver list */
rt_list_insert_after(&_protocal_list, &(protocal->list));
return RT_EOK;
}
/**
* This function is the callback function of hid's int endpoint, it is invoked when data comes.
*
* @param context the context of the callback function.
*
* @return none.
*/
static void rt_usbh_hid_callback(void* context)
{
upipe_t pipe;
struct uhid* hid;
int timeout = USB_TIMEOUT_LONG;
/* parameter check */
RT_ASSERT(context != RT_NULL);
pipe = (upipe_t)context;
hid = (struct uhid*)pipe->user_data;
rt_kprintf("%s()\n", __func__);
/* This function is not used in HID protocal?! */
/* invoke protocal callback function */
hid->protocal->callback((void*)hid);
/* parameter check */
RT_ASSERT(pipe->inst->hcd != RT_NULL);
rt_usb_hcd_pipe_xfer(pipe->inst->hcd, pipe, hid->buffer,
pipe->ep.wMaxPacketSize, timeout);
}
/**
* This function will find specified hid protocal from protocal list
*
* @param pro_id the protocal id.
*
* @return the found protocal or RT_NULL if there is no this protocal.
*/
static uprotocal_t rt_usbh_hid_protocal_find(int pro_id)
{
struct rt_list_node *node;
/* try to find protocal object */
for (node = _protocal_list.next; node != &_protocal_list; node = node->next)
{
uprotocal_t protocal = (uprotocal_t)rt_list_entry(node, struct uprotocal, list);
rt_kprintf("protocal->pro_id=%d\n", protocal->pro_id);
if (protocal->pro_id == pro_id) return protocal;
}
/* not found */
return RT_NULL;
}
/**
* This function will run hid class driver when usb device is detected and identified
* as a hid class device, it will continue the enumulate process.
*
* @param arg the argument.
*
* @return the error code, RT_EOK on successfully.
*/
static rt_err_t rt_usbh_hid_enable(void* arg)
{
int i = 0, pro_id;
uprotocal_t protocal;
struct uhid* hid;
struct uhintf* intf = (struct uhintf*)arg;
int timeout = USB_TIMEOUT_BASIC;
upipe_t pipe;
/* parameter check */
if(intf == RT_NULL)
{
rt_kprintf("the interface is not available\n");
return -RT_EIO;
}
pro_id = intf->intf_desc->bInterfaceProtocol;
RT_DEBUG_LOG(RT_DEBUG_USB, ("HID device enable, protocal id %d\n", pro_id));
protocal = rt_usbh_hid_protocal_find(pro_id);
if(protocal == RT_NULL)
{
rt_kprintf("can't find hid protocal %d\n", pro_id);
intf->user_data = RT_NULL;
return -RT_ERROR;
}
hid = rt_malloc(sizeof(struct uhid));
RT_ASSERT(hid != RT_NULL);
/* initilize the data structure */
rt_memset(hid, 0, sizeof(struct uhid));
intf->user_data = (void*)hid;
hid->protocal = protocal;
for(i=0; i<intf->intf_desc->bNumEndpoints; i++)
{
rt_err_t ret;
uep_desc_t ep_desc;
/* get endpoint descriptor */
rt_usbh_get_endpoint_descriptor(intf->intf_desc, i, &ep_desc);
if(ep_desc == RT_NULL)
{
rt_kprintf("rt_usbh_get_endpoint_descriptor error\n");
return -RT_ERROR;
}
if(USB_EP_ATTR(ep_desc->bmAttributes) != USB_EP_ATTR_INT)
continue;
if(!(ep_desc->bEndpointAddress & USB_DIR_IN)) continue;
ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &hid->pipe_in, intf->device, ep_desc);
if(ret != RT_EOK) return ret;
}
/* initialize hid protocal */
hid->protocal->init((void*)intf);
#if 0
pipe = hid->pipe_in;
/* parameter check */
RT_ASSERT(pipe->inst->hcd != RT_NULL);
rt_usb_hcd_pipe_xfer(pipe->inst->hcd, pipe,
hid->buffer, pipe->ep.wMaxPacketSize, timeout);
/* 这里直接读取一个报告?按照HID规范,读报告都要先设定报告指令,再读取,因为
一般HID设备都有多个报告, 所以这里直接读取的行为不合适.只有mouse类HID设备
才可能使用这种方式,驱动应该是适应多数设备的,所以这里屏蔽掉! */
#endif
return RT_EOK;
}
/**
* This function will be invoked when usb device plug out is detected and it would clean
* and release all hub class related resources.
*
* @param arg the argument.
*
* @return the error code, RT_EOK on successfully.
*/
static rt_err_t rt_usbh_hid_disable(void* arg)
{
struct uhid* hid;
struct uhintf* intf = (struct uhintf*)arg;
RT_ASSERT(intf != RT_NULL);
RT_DEBUG_LOG(RT_DEBUG_USB, ("rt_usbh_hid_disable\n"));
hid = (struct uhid*)intf->user_data;
if(hid != RT_NULL)
{
if(hid->pipe_in != RT_NULL)
{
/* free the HID in pipe */
rt_usb_hcd_free_pipe(intf->device->hcd, hid->pipe_in);
}
/* free the hid instance */
rt_free(hid);
}
/* free the instance */
rt_free(intf);
return RT_EOK;
}
/**
* This function will register hid class driver to the usb class driver manager.
* and it should be invoked in the usb system initialization.
*
* @return the error code, RT_EOK on successfully.
*/
ucd_t rt_usbh_class_driver_hid(void)
{
rt_list_init(&_protocal_list);
hid_driver.class_code = USB_CLASS_HID;
hid_driver.enable = rt_usbh_hid_enable;
hid_driver.disable = rt_usbh_hid_disable;
return &hid_driver;
}
#endif
/*
* Copyright (c) 2006-2018, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2011-12-12 Yi Qiu first version
*/
#ifndef __HID_H__
#define __HID_H__
#include <rtthread.h>
struct uhid
{
upipe_t pipe_in;
rt_uint8_t buffer[8];
uprotocal_t protocal;
};
typedef struct uhid uhid_t;
#define USB_REQ_GET_REPORT 0x01
#define USB_REQ_GET_IDLE 0x02
#define USB_REQ_GET_PROTOCOL 0x03
#define USB_REQ_SET_REPORT 0x09
#define USB_REQ_SET_IDLE 0x0a
#define USB_REQ_SET_PROTOCOL 0x0b
#define USB_HID_TOUCH 0
#define USB_HID_KEYBOARD 1
#define USB_HID_MOUSE 2
rt_err_t rt_usbh_hid_set_idle(struct uhintf* intf, int duration, int report_id);
rt_err_t rt_usbh_hid_get_report(struct uhintf* intf, rt_uint8_t type, rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size);
rt_err_t rt_usbh_hid_set_report(struct uhintf* intf, rt_uint8_t type, rt_uint8_t id, rt_uint8_t *buffer, rt_size_t size);
rt_err_t rt_usbh_hid_set_protocal(struct uhintf* intf, int protocol);
rt_err_t rt_usbh_hid_get_report_descriptor(struct uhintf* intf, rt_uint8_t *buffer, rt_size_t size);
rt_err_t rt_usbh_hid_protocal_register(uprotocal_t protocal);
ucd_t rt_usbh_class_driver_hid(void);
#endif
举例应用方法,我这里以读取USB触摸屏info为例:
static rt_uint8_t outbuf[64];
void ilitek_read_tp_info(struct uhintf* intf)
{
rt_uint8_t buf[64] = {0x03, 0xA3, 1, 5, 0x61}; //0x61
/* parameter2&3 varies from device to device!! */
if (rt_usbh_hid_set_report(intf, 0x02, 0x03, buf, 5) == RT_EOK) {
rt_kprintf("set report 0x61 OK\n");
if (rt_usbh_hid_get_report(intf, 0x02, 0x03, outbuf, 9) == RT_EOK) {
rt_kprintf("get report 0x61 OK\n");
/* In outbuf is the returned data from HID device */
/* 03 A3 61 05 11 25 0D 00 01 */ /* my touch chip is ili2511 */
}
}
}
参考umouse.c实现touch的接口函数,
static rt_err_t rt_usbh_hid_touch_init(void* arg)
{
struct uhintf* intf = (struct uhintf*)arg;
RT_ASSERT(intf != RT_NULL);
rt_usbh_hid_set_protocal(intf, 0); /* 指定HID设备的协议, 协议号从接口描述符中获取 */
rt_usbh_hid_set_idle(intf, 10, 0);
#ifdef RT_USING_RTGUI
//EVENT_MOUSE_BUTTON_INIT(&etouch); /* etouch是我的触摸屏的数据接口,因设备而异 */
#endif
ilitek_read_tp_info(intf);
return RT_EOK;
}
/**
* This function will define the hid touch protocal, it will be register to the protocal list.
*
* @return the touch protocal structure.
*/
uprotocal_t rt_usbh_hid_protocal_touch(void)
{
touch_protocal.pro_id = USB_HID_TOUCH;
touch_protocal.init = rt_usbh_hid_touch_init;
touch_protocal.callback = rt_usbh_hid_touch_callback;
return &touch_protocal;
}
在usbhost.c中的rt_usb_host_init()接口中加入HID相关初始化
rt_err_t rt_usb_host_init(void)
{
ucd_t drv;
rt_device_t uhc;
uprotocal_t protocal;
uhc = rt_device_find(USB_HOST_CONTROLLER_NAME);
if(uhc == RT_NULL)
{
rt_kprintf("can't find usb host controller %s\n", USB_HOST_CONTROLLER_NAME);
return -RT_ERROR;
}
/* initialize usb hub */
rt_usbh_hub_init((uhcd_t)uhc);
/* initialize class driver */
rt_usbh_class_driver_init();
#ifdef RT_USBH_MSTORAGE
/* register mass storage class driver */
drv = rt_usbh_class_driver_storage();
rt_usbh_class_driver_register(drv);
#endif
/* register hub class driver */
drv = rt_usbh_class_driver_hub();
rt_usbh_class_driver_register(drv);
drv = rt_usbh_class_driver_hid();
rt_usbh_class_driver_register(drv);
#ifdef RT_USBH_HID_MOUSE
protocal = rt_usbh_hid_protocal_mouse();
rt_usbh_hid_protocal_register(protocal);
#endif
#ifdef RT_USBH_HID_TOUCH
protocal = rt_usbh_hid_protocal_touch();
rt_usbh_hid_protocal_register(protocal);
#endif
/* initialize usb host controller */
rt_device_init(uhc);
return RT_EOK;
}
有个接口函数
/**
* This function is the callback function of hid's int endpoint, it is invoked when data comes.
*
* @param context the context of the callback function.
*
* @return none.
*/
static void rt_usbh_hid_callback(void *context);
没有搞懂应该在哪里调用它,我的理解是,在用户处理数据的线程中调用这样的一个接口,看注释该接口的设定是由中断调用的,但是中断处理部分已经由drv_usbh.c的drv_pipe_xfer()接口处理好了.所以这里很迷惑!
rt_err_t rt_usbh_hid_set_report(struct uintf* intf, rt_uint8_t *buffer, rt_size_t size)
请问,为什么我这边的驱动是这个样子的,然后这边找不到uintf结构体
这个估计是个老的代码遗留的吧.在那个 rt_usbh_hid_enable函数中看到这个调用. ret = rt_usb_hcd_alloc_pipe(intf->device->hcd, &hid->pipe_in,intf, ep_desc, rt_usbh_hid_callback);