static void acm_free_func(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); kobject_del(&acm->kobj); kobject_put(&acm->kobj); kfree(acm); }
static void acm_free_func(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); D("+\n"); kfree(acm); D("-\n"); }
static void acm_disable(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); struct usb_composite_dev *cdev = f->config->cdev; DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); gserial_disconnect(&acm->port); usb_ep_disable(acm->notify); acm->notify->driver_data = NULL; }
static void acm_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_acm *acm = func_to_acm(f); D("+\n"); acm_string_defs[0].id = 0; usb_free_all_descriptors(f); if (acm->notify_req) gs_acm_cdev_free_req(acm->notify, acm->notify_req); D("-\n"); }
static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_acm *acm = func_to_acm(f); struct usb_composite_dev *cdev = f->config->cdev; /* we know alt == 0, so this is an activation or a reset */ if (intf == acm->ctrl_id) { if (acm->notify->driver_data) { VDBG(cdev, "reset acm control interface %d\n", intf); usb_ep_disable(acm->notify); } if (!acm->notify->desc) if (config_ep_by_speed(cdev->gadget, f, acm->notify)) return -EINVAL; usb_ep_enable(acm->notify); acm->notify->driver_data = acm; } else if (intf == acm->data_id) { if (acm->port.in->driver_data) { DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); gserial_disconnect(&acm->port); } if (!acm->port.in->desc || !acm->port.out->desc) { DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); if (config_ep_by_speed(cdev->gadget, f, acm->port.in) || config_ep_by_speed(cdev->gadget, f, acm->port.out)) { acm->port.in->desc = NULL; acm->port.out->desc = NULL; return -EINVAL; } } gserial_connect(&acm->port, acm->port_num); } else return -EINVAL; return 0; }
static void acm_disable(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); struct usb_composite_dev *cdev = f->config->cdev; D("+\n"); DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num); gacm_cdev_disconnect(&acm->port); if (acm->notify) { usb_ep_disable(acm->notify); acm->notify->driver_data = NULL; } bsp_usb_set_enum_stat(acm->data_id, 0); D("-\n"); }
/* 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 acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { struct f_acm *acm = func_to_acm(f); struct usb_composite_dev *cdev = f->config->cdev; struct usb_request *req = cdev->req; int value = -EOPNOTSUPP; u16 w_index = le16_to_cpu(ctrl->wIndex); u16 w_value = le16_to_cpu(ctrl->wValue); u16 w_length = le16_to_cpu(ctrl->wLength); /* composite driver infrastructure handles everything except * CDC class messages; interface activation uses set_alt(). * * Note CDC spec table 4 lists the ACM request profile. It requires * encapsulated command support ... we don't handle any, and respond * to them by stalling. Options include get/set/clear comm features * (not that useful) and SEND_BREAK. */ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { /* SET_LINE_CODING ... just read and save what the host sends */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_REQ_SET_LINE_CODING: if (w_length != sizeof(struct usb_cdc_line_coding) || w_index != acm->ctrl_id) goto invalid; value = w_length; cdev->gadget->ep0->driver_data = acm; req->complete = acm_complete_set_line_coding; break; /* GET_LINE_CODING ... return what host sent, or initial value */ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_REQ_GET_LINE_CODING: if (w_index != acm->ctrl_id) goto invalid; value = min_t(unsigned, w_length, sizeof(struct usb_cdc_line_coding)); memcpy(req->buf, &acm->port_line_coding, value); break; /* SET_CONTROL_LINE_STATE ... save what the host sent */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_REQ_SET_CONTROL_LINE_STATE: if (w_index != acm->ctrl_id) goto invalid; value = 0; /* FIXME we should not allow data to flow until the * host sets the ACM_CTRL_DTR bit; and when it clears * that bit, we should return to that no-flow state. */ acm->port_handshake_bits = w_value; break; default: invalid: VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); } /* respond with data transfer or status phase? */ if (value >= 0) { DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", acm->port_num, ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); req->zero = 0; req->length = value; value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); if (value < 0) ERROR(cdev, "acm response on ttyGS%d, err %d\n", acm->port_num, value); } /* device either stalls (value < 0) or reports success */ return value; }
/* 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; }
static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_acm *acm = func_to_acm(f); struct usb_composite_dev *cdev = f->config->cdev; bool is_setting = 0; int ret; D("+\n"); /* we know alt == 0, so this is an activation or a reset */ /* if it is single interface, intf, acm->ctrl_id and acm->data_id * are the same, so we can setting data and notify interface in the same time. * * if it is multi interface, acm->ctrl_id and acm->data_id are different, * so the setting is go ahead in different times. */ if (intf == acm->ctrl_id) { is_setting = 1; if (acm->notify) { if (acm->notify->driver_data) { VDBG(cdev, "reset acm control interface %d\n", intf); usb_ep_disable(acm->notify); } else { VDBG(cdev, "init acm ctrl interface %d\n", intf); if (config_ep_by_speed(cdev->gadget, f, acm->notify)) return -EINVAL; } ret = usb_ep_enable(acm->notify); if (ret < 0) { ERROR(cdev, "Enable acm interface ep failed\n"); return ret; } acm->notify->driver_data = acm; } } if (intf == acm->data_id) { is_setting = 1; if (acm->port.in->driver_data) { DBG(cdev, "reset acm ttyGS%d\n", acm->port_num); gacm_cdev_disconnect(&acm->port); } if (!acm->port.in->desc || !acm->port.out->desc) { DBG(cdev, "activate acm ttyGS%d\n", acm->port_num); if (config_ep_by_speed(cdev->gadget, f, acm->port.in) || config_ep_by_speed(cdev->gadget, f, acm->port.out)) { acm->port.in->desc = NULL; acm->port.out->desc = NULL; return -EINVAL; } } gacm_cdev_connect(&acm->port, acm->port_num); bsp_usb_set_enum_stat(acm->data_id, 1); } if (!is_setting) return -EINVAL; D("-\n"); return 0; }
static void acm_resume(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); gacm_cdev_resume_notify(&acm->port); }
static void acm_suspend(struct usb_function *f) { struct f_acm *acm = func_to_acm(f); gacm_cdev_suspend_notify(&acm->port); }