/* ACM function driver setup/binding */ static int acm_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_acm *acm = func_to_acm(f); struct usb_string *us; int status; struct usb_ep *ep; acm_string_defs[ACM_CTRL_IDX].s = acm->ctrl_string_buf; acm_string_defs[ACM_DATA_IDX].s = acm->data_string_buf; acm_string_defs[ACM_IAD_IDX].s = acm->iad_string_buf; /* maybe allocate device-global string IDs, and patch descriptors */ us = usb_gstrings_attach(cdev, acm_strings, ARRAY_SIZE(acm_string_defs)); if (IS_ERR(us)) return PTR_ERR(us); acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id; acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id; acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id; acm_iad_descriptor.bFunctionProtocol = acm->iad_proto; acm_control_interface_desc.bInterfaceProtocol = acm->ctrl_intf_proto; /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) goto fail; acm->ctrl_id = status; acm_iad_descriptor.bFirstInterface = status; acm_control_interface_desc.bInterfaceNumber = status; acm_union_desc .bMasterInterface0 = status; status = usb_interface_id(c, f); if (status < 0) goto fail; acm->data_id = status; acm_data_interface_desc.bInterfaceNumber = status; acm_union_desc.bSlaveInterface0 = status; acm_call_mgmt_descriptor.bDataInterface = status; status = -ENODEV; /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); if (!ep) goto fail; acm->port.in = ep; ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); if (!ep) goto fail; acm->port.out = ep; ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); if (!ep) goto fail; acm->notify = ep; ep->driver_data = cdev; /* claim */ /* allocate notification */ acm->notify_req = gs_alloc_req(ep, sizeof(struct usb_cdc_notification) + 2, GFP_KERNEL); if (!acm->notify_req) goto fail; acm->notify_req->complete = acm_cdc_notify_complete; acm->notify_req->context = acm; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; acm_hs_notify_desc.bEndpointAddress = acm_fs_notify_desc.bEndpointAddress; acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, acm_ss_function); if (status) goto fail; DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", acm->port_num, gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", acm->port.in->name, acm->port.out->name, acm->notify->name); return 0; fail: if (acm->notify_req) { gs_free_req(acm->notify, acm->notify_req); acm->notify_req = NULL; } /* we might as well release our claims on endpoints */ if (acm->notify) acm->notify->driver_data = NULL; if (acm->port.out) acm->port.out->driver_data = NULL; if (acm->port.in) acm->port.in->driver_data = NULL; ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); return status; }
static int uvc_function_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct uvc_device *uvc = to_uvc(f); struct usb_string *us; unsigned int max_packet_mult; unsigned int max_packet_size; struct usb_ep *ep; struct f_uvc_opts *opts; int ret = -EINVAL; INFO(cdev, "uvc_function_bind\n"); opts = fi_to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); /* For SS, wMaxPacketSize has to be 1024 if bMaxBurst is not 0 */ if (opts->streaming_maxburst && (opts->streaming_maxpacket % 1024) != 0) { opts->streaming_maxpacket = roundup(opts->streaming_maxpacket, 1024); INFO(cdev, "overriding streaming_maxpacket to %d\n", opts->streaming_maxpacket); } /* Fill in the FS/HS/SS Video Streaming specific descriptors from the * module parameters. * * NOTE: We assume that the user knows what they are doing and won't * give parameters that their UDC doesn't support. */ if (opts->streaming_maxpacket <= 1024) { max_packet_mult = 1; max_packet_size = opts->streaming_maxpacket; } else if (opts->streaming_maxpacket <= 2048) { max_packet_mult = 2; max_packet_size = opts->streaming_maxpacket / 2; } else { max_packet_mult = 3; max_packet_size = opts->streaming_maxpacket / 3; } uvc_fs_streaming_ep.wMaxPacketSize = cpu_to_le16(min(opts->streaming_maxpacket, 1023U)); uvc_fs_streaming_ep.bInterval = opts->streaming_interval; uvc_hs_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size | ((max_packet_mult - 1) << 11)); uvc_hs_streaming_ep.bInterval = opts->streaming_interval; uvc_ss_streaming_ep.wMaxPacketSize = cpu_to_le16(max_packet_size); uvc_ss_streaming_ep.bInterval = opts->streaming_interval; uvc_ss_streaming_comp.bmAttributes = max_packet_mult - 1; uvc_ss_streaming_comp.bMaxBurst = opts->streaming_maxburst; uvc_ss_streaming_comp.wBytesPerInterval = cpu_to_le16(max_packet_size * max_packet_mult * (opts->streaming_maxburst + 1)); /* Allocate endpoints. */ ep = usb_ep_autoconfig(cdev->gadget, &uvc_control_ep); if (!ep) { INFO(cdev, "Unable to allocate control EP\n"); goto error; } uvc->control_ep = ep; if (gadget_is_superspeed(c->cdev->gadget)) ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, &uvc_ss_streaming_comp); else if (gadget_is_dualspeed(cdev->gadget)) ep = usb_ep_autoconfig(cdev->gadget, &uvc_hs_streaming_ep); else ep = usb_ep_autoconfig(cdev->gadget, &uvc_fs_streaming_ep); if (!ep) { INFO(cdev, "Unable to allocate streaming EP\n"); goto error; } uvc->video.ep = ep; uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; us = usb_gstrings_attach(cdev, uvc_function_strings, ARRAY_SIZE(uvc_en_us_strings)); if (IS_ERR(us)) { ret = PTR_ERR(us); goto error; } uvc_iad.iFunction = us[UVC_STRING_CONTROL_IDX].id; uvc_control_intf.iInterface = us[UVC_STRING_CONTROL_IDX].id; ret = us[UVC_STRING_STREAMING_IDX].id; uvc_streaming_intf_alt0.iInterface = ret; uvc_streaming_intf_alt1.iInterface = ret; /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) goto error; uvc_iad.bFirstInterface = ret; uvc_control_intf.bInterfaceNumber = ret; uvc->control_intf = ret; if ((ret = usb_interface_id(c, f)) < 0) goto error; uvc_streaming_intf_alt0.bInterfaceNumber = ret; uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; /* Copy descriptors */ f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL); if (IS_ERR(f->fs_descriptors)) { ret = PTR_ERR(f->fs_descriptors); f->fs_descriptors = NULL; goto error; } if (gadget_is_dualspeed(cdev->gadget)) { f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); if (IS_ERR(f->hs_descriptors)) { ret = PTR_ERR(f->hs_descriptors); f->hs_descriptors = NULL; goto error; } } if (gadget_is_superspeed(c->cdev->gadget)) { f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); if (IS_ERR(f->ss_descriptors)) { ret = PTR_ERR(f->ss_descriptors); f->ss_descriptors = NULL; goto error; } } /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL); if (uvc->control_req == NULL || uvc->control_buf == NULL) { ret = -ENOMEM; goto error; } uvc->control_req->buf = uvc->control_buf; uvc->control_req->complete = uvc_function_ep0_complete; uvc->control_req->context = uvc; if (v4l2_device_register(&cdev->gadget->dev, &uvc->v4l2_dev)) { printk(KERN_INFO "v4l2_device_register failed\n"); goto error; } /* Initialise video. */ ret = uvcg_video_init(&uvc->video); if (ret < 0) goto error; /* Register a V4L2 device. */ ret = uvc_register_video(uvc); if (ret < 0) { printk(KERN_INFO "Unable to register video device\n"); goto error; } return 0; error: v4l2_device_unregister(&uvc->v4l2_dev); if (uvc->control_req) usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); usb_free_all_descriptors(f); return ret; }
/* ACM function driver setup/binding */ static int acm_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_acm *acm = func_to_acm(f); struct usb_string *us; int status; struct usb_ep *ep; D("+\n"); /* REVISIT might want instance-specific strings to help * distinguish instances ... */ /* maybe allocate device-global string IDs, and patch descriptors */ us = usb_gstrings_attach(cdev, acm_strings, ARRAY_SIZE(acm_string_defs)); if (IS_ERR(us)) return PTR_ERR(us); acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id; acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id; acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id; /* allocate instance-specific interface IDs, and patch descriptors */ status = usb_interface_id(c, f); if (status < 0) goto fail; D("interface id: %d\n", status); if (g_acm_is_single_interface) { D("single interface\n"); acm->ctrl_id = acm->data_id = status; acm_single_interface_desc.bInterfaceNumber = status; acm_call_mgmt_descriptor.bDataInterface = status; } else { acm->ctrl_id = (u8)status; acm_iad_descriptor.bFirstInterface = status; acm_control_interface_desc.bInterfaceNumber = status; acm_union_desc .bMasterInterface0 = status; status = usb_interface_id(c, f); if (status < 0) goto fail; acm->data_id = status; acm_data_interface_desc.bInterfaceNumber = status; acm_union_desc.bSlaveInterface0 = status; acm_call_mgmt_descriptor.bDataInterface = status; } bsp_usb_add_setup_dev((unsigned)acm->data_id); status = -ENODEV; /* allocate instance-specific endpoints */ D("to ep autoconfig\n"); ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); if (!ep) goto fail; acm->port.in = ep; ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); if (!ep) goto fail; acm->port.out = ep; ep->driver_data = cdev; /* claim */ if (acm->support_notify) { ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); if (!ep) goto fail; acm->notify = ep; ep->driver_data = cdev; /* claim */ /* allocate notification */ acm->notify_req = gs_acm_cdev_alloc_req(ep, sizeof(struct usb_cdc_notification) + 2, GFP_KERNEL); if (!acm->notify_req) goto fail; acm->notify_req->complete = acm_cdc_notify_complete; acm->notify_req->context = acm; } else { acm->notify = NULL; acm->notify_req = NULL; } /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ D("do desc\n"); acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; if (acm->support_notify) acm_hs_notify_desc.bEndpointAddress = acm_fs_notify_desc.bEndpointAddress; acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; D("to assign desc\n"); acm_set_config_vendor(acm); status = usb_assign_descriptors(f, acm_fs_cur_function, acm_hs_cur_function, acm_ss_cur_function); if (status) goto fail; DBG(cdev, "acm_cdev%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", acm->port_num, gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", acm->port.in->name, acm->port.out->name, acm->notify ? acm->notify->name : "null"); printk(KERN_INFO "acm_cdev%d: %s speed IN/%s OUT/%s NOTIFY/%s\n", acm->port_num, gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", acm->port.in->name, acm->port.out->name, acm->notify ? acm->notify->name : "null"); return 0; fail: if (acm->notify_req) gs_acm_cdev_free_req(acm->notify, acm->notify_req); /* we might as well release our claims on endpoints */ if (acm->notify) acm->notify->driver_data = NULL; if (acm->port.out) acm->port.out->driver_data = NULL; if (acm->port.in) acm->port.in->driver_data = NULL; ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); D("-\n"); return status; }