Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1958864
  • 博文数量: 1000
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7921
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-20 09:23
个人简介

storage R&D guy.

文章分类

全部博文(1000)

文章存档

2019年(5)

2017年(47)

2016年(38)

2015年(539)

2014年(193)

2013年(178)

分类: 服务器与存储

2015-05-31 11:30:20

最近在研究hid通信,下面总结下供以后参考,同时也希望能给正在研究这个的同胞提供一些帮助。

有关自定义hid设备在linux内核源码下的Documentation/usb/gadget_hid.txt中有相关说明:如何添加自定义描述符,以及应用程序,首先对这个说明做一些补充.

下面我以s3c2416进行说明,内核源码linux3.1.0

hid相关源码在linux内核源码下的drivers/usg/gadget里,主要包括的文件的有:hid.c,f_hid.c,composite.c,s3c-hsudc.c.

首先在hid.c里添加关于你自定义的设备的描述符:


  1. "font-size:18px;">#include   
  2.   
  3. /* hid descriptor for a keyboard */  
  4. static struct hidg_func_descriptor my_hid_data = {  
  5.     .subclass       = 0, /* No subclass */  
  6.     .protocol       = 0, /* User define */  
  7.     .report_length      = 64,  
  8.     .report_desc_length = 28,  
  9.     .report_desc        = {  
  10.   
  11.     }  
  12. };  
  13.   
  14. static struct platform_device my_hid = {  
  15.     .name           = "hidg",  
  16.     .id         = 0,  
  17.     .num_resources      = 0,  
  18.     .resource       = 0,  
  19.     .dev.platform_data  = &my_hid_data,  
  20. };  
具体内容我就不填进去了,添加这个后,在函数:


static int __init hidg_init(void)

中添加如下代码:

  1. status = platform_device_register(&my_hid);  
  2. if(status < 0) {  
  3.     printk("++++++++++++++++reg failed\n");  
  4.     platform_device_unregister(&my_hid);  
  5.     return status;  
  6. }  
如果你的硬件部分没有问题,重新编译内核烧写电脑就应该可以识别了。但是2416的配制部分还有一点问题:


在s3c-hsudc.c中找到S3C2443_PHYCTRL

然后直接给他写成0

  1. writel(0, S3C2443_PHYCTRL);  
这样修改后还是不成功,你可以下载一个Bus Hound的软件进行分析,这里我就不具体说了。


然后在f_hid.c中找到:static int hidg_setup(struct usb_function *f,const struct usb_ctrlrequest *ctrl)函数,文件第324行

在变量声明后加入如下代码(第338行加入)::

  1.         if(((ctrl->bRequestType << 8) | ctrl->bRequest) != ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8  
  2.                                                                   | HID_REQ_SET_REPORT)) {  
  3.             mdelay(1);  
  4.         }  
  5.         else  
  6.             udelay(100);  
这里把set_report的时间间隔改成100us即你用2416给pc发数据的时间间隔,其它的设置成1ms,这里不一定要是1ms你可以改小一点。


修改完成后pc就可以识别它了。经过测试这个只能用于小数据的通信,就是说用pc每次向2416发送64Bytes是没有问题的,但是要是传文件的话就会丢包,而且很严重,如果pc每次向2416发64Bytes,然后2416回一个收到的指令给pc,反复这样传估计是没问题的,具体我没测试。

我要的效果是pc能给2416传文件,把文件封装成由多个9点几K组成的包然后传给2416,即pc每给2416传9点几K,然后2416给pc回一个指令,然后pc接着传,通过上面这样修改后这个效果是达不到的,所以我又做了如下修改,主要修改文件为f_hid.c.

Linux里有一种循环缓冲区kfifo,这个网上有很多说明,我这里就不说了,我主要说下我增加了些什么.首先增加一个kfifo.定义buffer大小,buffer大小16K,丢包的原因是因为pc端数据发送太快,而2416这边应用程序需要对数据进行处理反应不过来,所以需要增加这个动态缓冲区

static struct kfifo recv_fifo;

#define RECV_LEN    (16*1024)

定义在文件开头即可.

然后在static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)函数中添加如下代码:

  1. //add by hclydao  
  2.     status = kfifo_alloc(&recv_fifo, RECV_LEN, GFP_KERNEL);  
  3.     if (status) {  
  4.         printk(KERN_ERR "+++++++++++ error kfifo_alloc\n");  
  5.         goto fail;  
  6.     }  
在static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)中添加: 


  1. kfifo_free(&recv_fifo);  
在static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)函数中添加:


kfifo_in(&recv_fifo, req->buf, req->actual);位置如下:

  1. hidg->set_report_length = req->actual;  
  2. memcpy(hidg->set_report_buff, req->buf, req->actual);  
  3. //hclydao  
  4. //mutex_lock(&recv_lock);  
  5. kfifo_in(&recv_fifo, req->buf, req->actual);  
  6. //mutex_unlock(&recv_lock);  
  7. spin_unlock(&hidg->spinlock);  

然后在static ssize_t f_hidg_read(struct file *file, char __user *buffer,
            size_t count, loff_t *ptr)函数中将


#define READ_COND (hidg->set_report_buff != NULL)这个定义修改为:

#define READ_COND (!kfifo_is_empty(&recv_fifo))

同时增加如下代码,首先增加声明:

unsigned int copied;

  1. if (tmp_buff != NULL) {  
  2.     /* copy to user outside spinlock */  
  3.     count -= copy_to_user(buffer, tmp_buff, count);  
  4.     kfree(tmp_buff);  
  5. else  
  6.     count = -ENOMEM;  
  7. //add by hclydao  
  8. //mutex_lock(&recv_lock);  
  9. count = kfifo_to_user(&recv_fifo,buffer,hidg->set_report_length,&copied);  
  10. //mutex_unlock(&recv_lock);  
  11. //count = kfifo_out(&recv_fifo,buffer,count,&copied);  
  12. //return count;  
  13. return copied;  
//add by hclydao后面是增加的代码.


修改完成后重新编译。

经过测试传送2M以下的文件是没有问题的,但是2M以上传送失败,速度大概在40-50KB/s,理论速度应该是64KB/s但是实际上受你的应用程序还有其它一些因素的影响不可能达到这么多.

对于为什么传2M以上的文件不行,继续纠结中。希望高手指点.

补充:最开始的时候还发现:

s3c-hsudc setup failed, returned -95的错误但是没影响正常使用 所以我就没说 觉得还是说一下好

可能是哪个请求没有完成,打开f_hid.c里的hidg_setup中的default里的       

VDBG(cdev, "Unknown request 0x%x\n",ctrl->bRequest);

编译后重新烧写你会发现提示

Unknown request 0xa果然和s3c6410一样,这个是host向设备发送get_interface设备没有回所以出现这个错误

具体定义在linux内核源码include/linux/usb/ch9.ch查找0x0A你就会找到

#define USB_REQ_GET_INTERFACE        0x0A

所以我们在hidg_setup中加入对get_interface的处理:

  1. case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8  
  2.       | USB_REQ_GET_INTERFACE):  
  3.     VDBG(cdev, "get_interface\n");  
  4.     /* send an empty report */  
  5.     length = min_t(unsigned, length, hidg->report_length);  
  6.     memset(req->buf, 0x0, length);  
  7.   
  8.     goto respond;  
  9.     break;  
当host发get_interface的时候,我们发个空包回去。


这样问题就解决了

2M文件上传不成功,经过加打印信息,发现是PC端程序的问题,但是我这改不了,所以没办法验证

阅读(2413) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~