Generate descriptors in new format and attach them to USB function in prep_descs(). Implement prep_vendor_descs() to supply class specific descriptors. Change set_alt() implementation and implement clear_alt() operation. Move cdev initialization to hidg_alloc(). Remove boilerplate code.
Signed-off-by: Robert Baldyga <r.bald...@samsung.com> --- drivers/usb/gadget/function/f_hid.c | 305 ++++++++++++++---------------------- 1 file changed, 119 insertions(+), 186 deletions(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 0456a53..8770289 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -125,14 +125,6 @@ static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { */ }; -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, - (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, - NULL, -}; - /* Full-Speed Support */ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { @@ -159,13 +151,16 @@ static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { */ }; -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, - (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, - NULL, -}; +USB_COMPOSITE_ENDPOINT(ep_in, &hidg_fs_in_ep_desc, + &hidg_hs_in_ep_desc, NULL, NULL); +USB_COMPOSITE_ENDPOINT(ep_out, &hidg_fs_out_ep_desc, + &hidg_hs_out_ep_desc, NULL, NULL); + +USB_COMPOSITE_ALTSETTING(intf0alt0, &hidg_interface_desc, &ep_in, &ep_out); + +USB_COMPOSITE_INTERFACE(intf0, &intf0alt0); + +USB_COMPOSITE_DESCRIPTORS(hidg_descs, &intf0); /*-------------------------------------------------------------------------*/ /* Strings */ @@ -487,27 +482,6 @@ respond: return status; } -static void hidg_disable(struct usb_function *f) -{ - struct f_hidg *hidg = func_to_hidg(f); - struct f_hidg_req_list *list, *next; - int i; - - usb_ep_disable(hidg->in_ep); - usb_ep_disable(hidg->out_ep); - - list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { - list_del(&list->list); - kfree(list); - } - - for (i = 0; i < hidg->qlen; ++i) { - kfree(hidg->out_reqs[i]->buf); - kfree(hidg->out_reqs[i]); - } - kfree(hidg->out_reqs); -} - static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct usb_composite_dev *cdev = f->config->cdev; @@ -516,65 +490,46 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); - if (hidg->in_ep != NULL) { - /* restart endpoint */ - usb_ep_disable(hidg->in_ep); + hidg->in_ep = usb_function_get_ep(f, intf, 0); + if (!hidg->in_ep) + return -ENODEV; + hidg->in_ep->driver_data = hidg; - 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 IN endpoint FAILED!\n"); - goto fail; - } - hidg->in_ep->driver_data = hidg; - } + hidg->out_ep = usb_function_get_ep(f, intf, 1); + if (!hidg->out_ep) + return -ENODEV; + hidg->out_ep->driver_data = hidg; + /* preallocate request and buffer */ + hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); + if (!hidg->req) + return -ENOMEM; - if (hidg->out_ep != NULL) { - /* restart endpoint */ - usb_ep_disable(hidg->out_ep); + hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); + if (!hidg->req->buf) + return -ENOMEM; - 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 IN endpoint FAILED!\n"); - goto fail; - } - hidg->out_ep->driver_data = hidg; - - /* - * allocate a bunch of read buffers and queue them all at once. - */ - hidg->out_reqs = kzalloc(hidg->qlen * - sizeof(*hidg->out_reqs), GFP_KERNEL); - for (i = 0; i < hidg->qlen && status == 0; i++) { - struct usb_request *req = - hidg_alloc_ep_req(hidg->out_ep, - hidg->report_length); - if (req) { + /* + * allocate a bunch of read buffers and queue them all at once. + */ + hidg->out_reqs = kzalloc(hidg->qlen * + sizeof(*hidg->out_reqs), GFP_KERNEL); + for (i = 0; i < hidg->qlen && status == 0; i++) { + struct usb_request *req = + hidg_alloc_ep_req(hidg->out_ep, + hidg->report_length); + if (req) { hidg->out_reqs[i] = req; - req->complete = hidg_set_report_complete; - req->context = hidg; - status = usb_ep_queue(hidg->out_ep, req, - GFP_ATOMIC); - if (status) - ERROR(cdev, "%s queue req --> %d\n", + req->complete = hidg_set_report_complete; + req->context = hidg; + status = usb_ep_queue(hidg->out_ep, req, + GFP_ATOMIC); + if (status) + ERROR(cdev, "%s queue req --> %d\n", hidg->out_ep->name, status); - } else { - usb_ep_disable(hidg->out_ep); - status = -ENOMEM; - goto free_req; - } + } else { + status = -ENOMEM; + goto free_req; } } @@ -587,10 +542,31 @@ free_req: kfree(hidg->out_reqs); } -fail: return status; } +static void hidg_clear_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_hidg *hidg = func_to_hidg(f); + struct f_hidg_req_list *list, *next; + int i; + + list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { + list_del(&list->list); + kfree(list); + } + + for (i = 0; i < hidg->qlen; ++i) { + kfree(hidg->out_reqs[i]->buf); + kfree(hidg->out_reqs[i]); + } + kfree(hidg->out_reqs); + + /* disable/free request and end point */ + kfree(hidg->req->buf); + usb_ep_free_request(hidg->in_ep, hidg->req); +} + static const struct file_operations f_hidg_fops = { .owner = THIS_MODULE, .open = f_hidg_open, @@ -601,50 +577,19 @@ static const struct file_operations f_hidg_fops = { .llseek = noop_llseek, }; -static int hidg_bind(struct usb_configuration *c, struct usb_function *f) +static int hidg_prep_descs(struct usb_function *f) { - struct usb_ep *ep; + struct usb_composite_dev *cdev = f->config->cdev; struct f_hidg *hidg = func_to_hidg(f); struct usb_string *us; - struct device *device; - int status; - dev_t dev; /* maybe allocate device-global string IDs, and patch descriptors */ - us = usb_gstrings_attach(c->cdev, ct_func_strings, + us = usb_gstrings_attach(cdev, ct_func_strings, ARRAY_SIZE(ct_func_string_defs)); if (IS_ERR(us)) return PTR_ERR(us); hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id; - /* 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; - hidg->in_ep = ep; - - ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); - if (!ep) - goto fail; - hidg->out_ep = ep; - - /* 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; @@ -652,6 +597,14 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); + + return usb_function_set_descs(f, &hidg_descs); +} + +static int hidg_prep_vendor_descs(struct usb_function *f) +{ + struct f_hidg *hidg = func_to_hidg(f); + /* * We can use hidg_desc struct here but we should not relay * that its content won't change after returning from this function. @@ -660,50 +613,10 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) hidg_desc.desc[0].wDescriptorLength = cpu_to_le16(hidg->report_desc_length); - hidg_hs_in_ep_desc.bEndpointAddress = - hidg_fs_in_ep_desc.bEndpointAddress; - hidg_hs_out_ep_desc.bEndpointAddress = - hidg_fs_out_ep_desc.bEndpointAddress; - - status = usb_assign_descriptors(f, hidg_fs_descriptors, - hidg_hs_descriptors, NULL); - if (status) - goto fail; - - mutex_init(&hidg->lock); - spin_lock_init(&hidg->spinlock); - init_waitqueue_head(&hidg->write_queue); - init_waitqueue_head(&hidg->read_queue); - INIT_LIST_HEAD(&hidg->completed_out_req); - - /* 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_free_descs; - - device = device_create(hidg_class, NULL, dev, NULL, - "%s%d", "hidg", hidg->minor); - if (IS_ERR(device)) { - status = PTR_ERR(device); - goto del; - } + usb_altset_add_vendor_desc(f, 0, 0, + (struct usb_descriptor_header *)&hidg_desc); return 0; -del: - cdev_del(&hidg->cdev); -fail_free_descs: - usb_free_all_descriptors(f); -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); - } - - return status; } static inline int hidg_get_minor(void) @@ -914,6 +827,10 @@ static void hidg_free(struct usb_function *f) hidg = func_to_hidg(f); opts = container_of(f->fi, struct f_hid_opts, func_inst); + + device_destroy(hidg_class, MKDEV(major, hidg->minor)); + cdev_del(&hidg->cdev); + kfree(hidg->report_desc); kfree(hidg); mutex_lock(&opts->lock); @@ -921,31 +838,25 @@ static void hidg_free(struct usb_function *f) mutex_unlock(&opts->lock); } -static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_hidg *hidg = func_to_hidg(f); - - device_destroy(hidg_class, MKDEV(major, hidg->minor)); - cdev_del(&hidg->cdev); - - /* disable/free request and end point */ - usb_ep_disable(hidg->in_ep); - kfree(hidg->req->buf); - usb_ep_free_request(hidg->in_ep, hidg->req); - - usb_free_all_descriptors(f); -} - static struct usb_function *hidg_alloc(struct usb_function_instance *fi) { struct f_hidg *hidg; struct f_hid_opts *opts; + struct device *device; + dev_t dev; + int ret; /* allocate and initialize one new instance */ hidg = kzalloc(sizeof(*hidg), GFP_KERNEL); if (!hidg) return ERR_PTR(-ENOMEM); + mutex_init(&hidg->lock); + spin_lock_init(&hidg->spinlock); + init_waitqueue_head(&hidg->write_queue); + init_waitqueue_head(&hidg->read_queue); + INIT_LIST_HEAD(&hidg->completed_out_req); + opts = container_of(fi, struct f_hid_opts, func_inst); mutex_lock(&opts->lock); @@ -961,26 +872,48 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) opts->report_desc_length, GFP_KERNEL); if (!hidg->report_desc) { - kfree(hidg); mutex_unlock(&opts->lock); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_hidg; } } mutex_unlock(&opts->lock); hidg->func.name = "hid"; - hidg->func.bind = hidg_bind; - hidg->func.unbind = hidg_unbind; + hidg->func.prep_descs = hidg_prep_descs; + hidg->func.prep_vendor_descs = hidg_prep_vendor_descs; hidg->func.set_alt = hidg_set_alt; - hidg->func.disable = hidg_disable; + hidg->func.clear_alt = hidg_clear_alt; hidg->func.setup = hidg_setup; hidg->func.free_func = hidg_free; /* this could me made configurable at some point */ hidg->qlen = 4; + /* create char device */ + cdev_init(&hidg->cdev, &f_hidg_fops); + dev = MKDEV(major, hidg->minor); + ret = cdev_add(&hidg->cdev, dev, 1); + if (ret < 0) + goto err_report; + + device = device_create(hidg_class, NULL, dev, NULL, + "%s%d", "hidg", hidg->minor); + if (IS_ERR(device)) { + ret = PTR_ERR(device); + goto err_cdev; + } + return &hidg->func; + +err_cdev: + cdev_del(&hidg->cdev); +err_report: + kfree(hidg->report_desc); +err_hidg: + kfree(hidg); + return ERR_PTR(ret); } DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc); -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html