分享

linux 自定义hid速度优化

 昵称28625772 2016-03-04

虽然能正常通信 但不速度太慢 默认只有控制端点IN/OUT和中断端点IN 而控制传输最大传输数据为64字节 基本上速度在300K左右 如果增加一个中断端点OUT默认数据就会出中断端点传输 中断端点高速设备最大传输为1024字节 相关说明在下面的参考文章中有详细的说明. 下面我说就来增加一个中断端点. 这个是在4418 linux3.4.39上增加的 其它linux版本 增加类似 先保证自定义hid设备能正常通信 然后开始增加 主要修改文件为内核drivers/usb/gadget/f_hid.c 和drivers/usb/gadget/hid.c

首先来看下f_hid.c
struct f_hidg {
 /* configuration */
 unsigned char   bInterfaceSubClass;
 unsigned char   bInterfaceProtocol;
 unsigned short   report_desc_length;
 char    *report_desc;
 unsigned short   report_length;
 /* recv report */
 char    *set_report_buff;
 unsigned short   set_report_length;
 spinlock_t   spinlock;
 wait_queue_head_t  read_queue;
 /* send report */
 struct mutex   lock;
 bool    write_pending;
 wait_queue_head_t  write_queue;
 struct usb_request  *req;
 int    minor;
 struct cdev   cdev;
 struct usb_function  func;
 struct usb_ep   *in_ep;
#ifdef CONFIG_INTOUT
 struct usb_ep   *out_ep;
 wait_queue_head_t read_wq;
 unsigned short rx_length;
 struct usb_request  *rx_req;
#endif
};
首先修改f_hidg结构增加#ifdef CONFIG_INTOUT中,中断OUT需要的参数.
修改struct usb_interface_descriptor hidg_interface_desc中bNumEndpoints
#ifdef CONFIG_INTOUT
 .bNumEndpoints  = 2,
#else
 .bNumEndpoints  = 1,
#endif
增加了中断OUT 端点数就变成了2
修改struct hid_descriptor hidg_desc中
.bcdHID    = 0x0200,
usb 2.0协议对应bcdhid是0x0200
增加高速度中断OUT端点描述符
#ifdef CONFIG_INTOUT
static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = {
 .bLength  = USB_DT_ENDPOINT_SIZE,
 .bDescriptorType = USB_DT_ENDPOINT,
 .bEndpointAddress = USB_DIR_OUT,
 .bmAttributes  = USB_ENDPOINT_XFER_INT,
 /*.wMaxPacketSize = DYNAMIC */
 .bInterval  = 1, /* FIXME: Add this field in the
          * HID gadget configuration?
          * (struct hidg_func_descriptor)
          */
};
#endif
这里bInterval是主机轮询时间1表示125us
修改struct usb_descriptor_header *hidg_hs_descriptors[]加上刚刚增加的endpoint

static struct usb_descriptor_header *hidg_hs_descriptors[] = {
 (struct usb_descriptor_header *)&hidg_interface_desc,
 (struct usb_descriptor_header *)&hidg_desc,
 (struct usb_descriptor_header *)&hidg_hs_in_ep_desc,
#ifdef CONFIG_INTOUT
 (struct usb_descriptor_header *)&hidg_hs_out_ep_desc,
#endif
 NULL,
};
hs也相应的加上中断out端点
#ifdef CONFIG_INTOUT
static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = {
 .bLength  = USB_DT_ENDPOINT_SIZE,
 .bDescriptorType = USB_DT_ENDPOINT,
 .bEndpointAddress = USB_DIR_OUT,
 .bmAttributes  = USB_ENDPOINT_XFER_INT,
 /*.wMaxPacketSize = DYNAMIC */
 .bInterval  = 1, /* FIXME: Add this field in the
           * HID gadget configuration?
           * (struct hidg_func_descriptor)
           */
};
#endif
static struct usb_descriptor_header *hidg_fs_descriptors[] = {
 (struct usb_descriptor_header *)&hidg_interface_desc,
 (struct usb_descriptor_header *)&hidg_desc,
 (struct usb_descriptor_header *)&hidg_fs_in_ep_desc,
#ifdef CONFIG_INTOUT
 (struct usb_descriptor_header *)&hidg_fs_out_ep_desc,
#endif
 NULL,
};
修改hidg_bind函数 增加对中端OUT的一些初始化
static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
{
    struct usb_ep        *ep;
    struct f_hidg        *hidg = func_to_hidg(f);
    int            status;
    dev_t            dev;
    /* allocate instance-specific interface IDs, and patch descriptors */
    status = usb_interface_id(c, f);
    if (status < 0)
        goto fail;
    hidg_interface_desc.bInterfaceNumber = status;

    /* allocate instance-specific endpoints */
    status = -ENODEV;
    ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc);
    if (!ep)
        goto fail;
    ep->driver_data = c->cdev;    /* claim */
    hidg->in_ep = ep;
#ifdef CONFIG_INTOUT
    status = -ENODEV;
    ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc);
    if (!ep)
        goto fail;
    ep->driver_data = c->cdev;    /* claim */
    hidg->out_ep = ep;
    /* preallocate request and buffer */
    status = -ENOMEM;
    hidg->rx_req = usb_ep_alloc_request(hidg->out_ep, GFP_KERNEL);
    if (!hidg->rx_req)
        goto fail;
    hidg->rx_req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
    if (!hidg->rx_req->buf)
        goto fail;
    hidg->rx_req->length = hidg->report_length;
    hidg->rx_length = hidg->report_length;
    hidg->rx_req->status   = 0;
    hidg->rx_req->zero     = 0;
    hidg->rx_req->context  = hidg;
    hidg->rx_req->complete = hidg_read_complete;
#endif
    /* preallocate request and buffer */
    status = -ENOMEM;
    hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL);
    if (!hidg->req)
        goto fail;

    hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL);
    if (!hidg->req->buf)
        goto fail;
    /* set descriptor dynamic values */
    hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass;
    hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
    hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
    hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(64);
#ifdef CONFIG_INTOUT
    hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length);
    hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(64);
#endif
    hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT;
    hidg_desc.desc[0].wDescriptorLength =
        cpu_to_le16(hidg->report_desc_length);
    hidg->set_report_buff = NULL;
    /* copy descriptors */
    f->descriptors = usb_copy_descriptors(hidg_fs_descriptors);
    if (!f->descriptors)
        goto fail;
    if (gadget_is_dualspeed(c->cdev->gadget)) {
        hidg_hs_in_ep_desc.bEndpointAddress =
            hidg_fs_in_ep_desc.bEndpointAddress;
#ifdef CONFIG_INTOUT
        hidg_hs_out_ep_desc.bEndpointAddress =
            hidg_fs_out_ep_desc.bEndpointAddress;
#endif
        f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors);
        if (!f->hs_descriptors)
            goto fail;
    }
    mutex_init(&hidg->lock);
    spin_lock_init(&hidg->spinlock);
    init_waitqueue_head(&hidg->write_queue);
    init_waitqueue_head(&hidg->read_queue);
#ifdef CONFIG_INTOUT
    init_waitqueue_head(&hidg->read_wq);
#endif
    /* create char device */
    cdev_init(&hidg->cdev, &f_hidg_fops);
    dev = MKDEV(major, hidg->minor);
    status = cdev_add(&hidg->cdev, dev, 1);
    if (status)
        goto fail;
    device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
#ifdef USE_FIFO
//add by hclydao
    status = kfifo_alloc(&recv_fifo, RECV_LEN, GFP_KERNEL);
    if (status) {
        printk(KERN_ERR "+++++++++++ error kfifo_alloc\n");
        goto fail;
    }
#endif
    return 0;
fail:
    ERROR(f->config->cdev, "hidg_bind FAILED\n");
    if (hidg->req != NULL) {
        kfree(hidg->req->buf);
        if (hidg->in_ep != NULL)
            usb_ep_free_request(hidg->in_ep, hidg->req);
    }
    usb_free_descriptors(f->hs_descriptors);
    usb_free_descriptors(f->descriptors);
    return status;
}
然后修改hidg_set_alt增加中断OUT的使能
static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
 struct usb_composite_dev  *cdev = f->config->cdev;
 struct f_hidg    *hidg = func_to_hidg(f);
 int status = 0;
 VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt);
 if (hidg->in_ep != NULL) {
  /* restart endpoint */
  if (hidg->in_ep->driver_data != NULL)
   usb_ep_disable(hidg->in_ep);
  status = config_ep_by_speed(f->config->cdev->gadget, f,
         hidg->in_ep);
  if (status) {
   ERROR(cdev, "config_ep_by_speed FAILED!\n");
   goto fail;
  }
  status = usb_ep_enable(hidg->in_ep);
  if (status < 0) {
   ERROR(cdev, "Enable endpoint FAILED!\n");
   goto fail;
  }
  hidg->in_ep->driver_data = hidg;
 }
#ifdef CONFIG_INTOUT
 if (hidg->out_ep != NULL) {
  /* restart endpoint */
  if (hidg->out_ep->driver_data != NULL)
   usb_ep_disable(hidg->out_ep);
  status = config_ep_by_speed(f->config->cdev->gadget, f,
         hidg->out_ep);
  if (status) {
   ERROR(cdev, "config_ep_by_speed FAILED!\n");
   goto fail;
  }
  status = usb_ep_enable(hidg->out_ep);
  if (status < 0) {
   ERROR(cdev, "Enable endpoint FAILED!\n");
   goto fail;
  }
  hidg->out_ep->driver_data = hidg;
 }
#endif
fail:
 return status;
}
我们先把
hidg->rx_req->complete = hidg_read_complete;
注释掉
一般到这里 编译完成下载就可以看到中断out端点了 此时如果使用bushond通过中断端点发送数据不同的平台就会有不同的现象 在这纠结了很久 4418如果到这里用bushond
通过中断OUT发送数据 发送不成功 可以通过USBlyzer监控 这时一直阻塞着 如果是2416第一次可以发送成功 但是第二次就成了阻塞状态了 最后找到原来是没有调用usb_ep_queue把out端点的相关处理加到队列中去
打开刚刚的注释 下面两个函数同时修改hidg_poll
#ifdef CONFIG_INTOUT
static ssize_t hidg_read(struct file *file, char __user *buffer,
   size_t count, loff_t *ptr)
{
 struct f_hidg *hidg     = file->private_data;
 char  *tmp_buff = NULL;
 unsigned long flags;
 unsigned int copied;
 if (!count)
  return 0;
 if (!access_ok(VERIFY_WRITE, buffer, count))
  return -EFAULT;
 spin_lock_irqsave(&hidg->spinlock, flags);
#ifndef USE_FIFO
#define READ_COND (hidg->set_report_buff != NULL)
#else
#define READ_COND (!kfifo_is_empty(&recv_fifo))
#endif
 while (!READ_COND) {
  spin_unlock_irqrestore(&hidg->spinlock, flags);
  if (file->f_flags & O_NONBLOCK)
   return -EAGAIN;
  if (wait_event_interruptible(hidg->read_wq, READ_COND))
   return -ERESTARTSYS;
  spin_lock_irqsave(&hidg->spinlock, flags);
 }
 count = min_t(unsigned, count, hidg->rx_length);
 tmp_buff = hidg->set_report_buff;
 hidg->set_report_buff = NULL;
 spin_unlock_irqrestore(&hidg->spinlock, flags);
 if (tmp_buff != NULL) {
  /* copy to user outside spinlock */
  count -= copy_to_user(buffer, tmp_buff, count);
  kfree(tmp_buff);
 } else
  count = -ENOMEM;
#ifdef USE_FIFO
 //add by hclydao
 count = kfifo_to_user(&recv_fifo,buffer,hidg->rx_length,&copied);
 return copied;
#else
 return count;
#endif
}
static void hidg_read_complete(struct usb_ep *ep, struct usb_request *req)
{
 struct f_hidg *hidg = (struct f_hidg *)req->context;
 int ret;
 if (req->status != 0 || req->buf == NULL || req->actual == 0) {
  ERROR(hidg->func.config->cdev,"%s FAILED\n", __func__);
  return;
 }
 spin_lock(&hidg->spinlock);
 //hclydao
 kfifo_in(&recv_fifo, req->buf, req->actual);
 spin_unlock(&hidg->spinlock);
 wake_up(&hidg->read_wq);
 hidg->rx_req->zero = 0;
 hidg->rx_req->length = hidg->rx_length;
 ret = usb_ep_queue(hidg->out_ep, hidg->rx_req, GFP_ATOMIC);
 if (ret < 0)
  ERROR(hidg->func.config->cdev, "usb_ep_queue error on ep0 %d\n", ret);
}
#endif
static unsigned int f_hidg_poll(struct file *file, poll_table *wait)
{
 struct f_hidg *hidg  = file->private_data;
 unsigned int ret = 0;
#ifdef CONFIG_INTOUT
 poll_wait(file, &hidg->read_wq, wait);
#else
 poll_wait(file, &hidg->read_queue, wait);
#endif
 poll_wait(file, &hidg->write_queue, wait);
 if (WRITE_COND)
  ret |= POLLOUT | POLLWRNORM;
 if (READ_COND)
  ret |= POLLIN | POLLRDNORM;
 return ret;
}
同时还需要修改hidg_setup函数
  case HID_DT_REPORT:
   VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n");
   length = min_t(unsigned short, length,
         hidg->report_desc_length);
   memcpy(req->buf, hidg->report_desc, length);
   if (usb_ep_queue(hidg->out_ep, hidg->rx_req, GFP_ATOMIC) < 0) {
    printk("+++usb_ep_queue error on ep out\n");
    return -1;
   }
   goto respond;
   break;
当hidg设备被识别后 需要queue一次out端点 才能正常发送数据 也可以在hidg_open中增加queue函数 但是热插拔就会出问题 所以加到这里.这样基本上就可以了 最后修改hid.c中的你自己的struct hidg_func_descriptor中
.report_length  = 1024,
同时修改.report_desc中的报告大小
0x75, 0x40,   
Report Size 64位以及
0x95, 0x80,
所有的REPORT_COUNT (128) 由于PC端的测试程序最后是读取这个里面的参数确定每次发送的数据大小 这样最后就是64x128=1024B
经测试速度可以达到900KB/s
 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多