虽然能正常通信 但不速度太慢 默认只有控制端点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 |
|
来自: 昵称28625772 > 《Linux HID》