f_serial.c revision fef6964452abae55c82e1615ac505b8377df00e9
161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* 261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * f_serial.c - generic USB serial function driver 361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * 461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) 561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Copyright (C) 2008 by David Brownell 661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Copyright (C) 2008 by Nokia Corporation 761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * 861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * This software is distributed under the terms of the GNU General 961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Public License ("GPL") as published by the Free Software Foundation, 1061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * either version 2 of that License or (at your option) any later version. 1161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 1261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 135a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 1461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include <linux/kernel.h> 1561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include <linux/device.h> 1661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 1761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include "u_serial.h" 1861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell#include "gadget_chips.h" 1961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 2061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 2161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* 2261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * This function packages a simple "generic serial" port with no real 2361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * control mechanisms, just raw data transfer over two bulk endpoints. 2461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * 2561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Because it's not standardized, this isn't as interoperable as the 2661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * CDC ACM driver. However, for many purposes it's just as functional 2761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * if you can arrange appropriate host side drivers. 2861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 2961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 3061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstruct f_gser { 3161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct gserial port; 3261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell u8 data_id; 3361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell u8 port_num; 3461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 3561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 3661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic inline struct f_gser *func_to_gser(struct usb_function *f) 3761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 3861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return container_of(f, struct f_gser, port.func); 3961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 4061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 4161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/ 4261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 4361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* interface descriptor: */ 4461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 4561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_interface_descriptor gser_interface_desc __initdata = { 4661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bLength = USB_DT_INTERFACE_SIZE, 4761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bDescriptorType = USB_DT_INTERFACE, 4861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* .bInterfaceNumber = DYNAMIC */ 4961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bNumEndpoints = 2, 5061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bInterfaceClass = USB_CLASS_VENDOR_SPEC, 5161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bInterfaceSubClass = 0, 5261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bInterfaceProtocol = 0, 5361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* .iInterface = DYNAMIC */ 5461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 5561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 5661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* full speed support: */ 5761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 5861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { 5961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 6061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bDescriptorType = USB_DT_ENDPOINT, 6161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bEndpointAddress = USB_DIR_IN, 6261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 6361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 6461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 6561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_fs_out_desc __initdata = { 6661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 6761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bDescriptorType = USB_DT_ENDPOINT, 6861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bEndpointAddress = USB_DIR_OUT, 6961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 7061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 7161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 7261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_descriptor_header *gser_fs_function[] __initdata = { 7361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell (struct usb_descriptor_header *) &gser_interface_desc, 7461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell (struct usb_descriptor_header *) &gser_fs_in_desc, 7561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell (struct usb_descriptor_header *) &gser_fs_out_desc, 7661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell NULL, 7761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 7861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 7961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* high speed support: */ 8061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 8161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { 8261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 8361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bDescriptorType = USB_DT_ENDPOINT, 8461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 85551509d267905705f6d723e51ec706916f06b859Harvey Harrison .wMaxPacketSize = cpu_to_le16(512), 8661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 8761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 8861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_endpoint_descriptor gser_hs_out_desc __initdata = { 8961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bLength = USB_DT_ENDPOINT_SIZE, 9061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bDescriptorType = USB_DT_ENDPOINT, 9161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .bmAttributes = USB_ENDPOINT_XFER_BULK, 92551509d267905705f6d723e51ec706916f06b859Harvey Harrison .wMaxPacketSize = cpu_to_le16(512), 9361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 9461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 9561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_descriptor_header *gser_hs_function[] __initdata = { 9661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell (struct usb_descriptor_header *) &gser_interface_desc, 9761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell (struct usb_descriptor_header *) &gser_hs_in_desc, 9861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell (struct usb_descriptor_header *) &gser_hs_out_desc, 9961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell NULL, 10061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 10161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 10261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* string descriptors: */ 10361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 10461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_string gser_string_defs[] = { 10561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell [0].s = "Generic Serial", 10661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell { } /* end of list */ 10761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 10861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 10961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_gadget_strings gser_string_table = { 11061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .language = 0x0409, /* en-us */ 11161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .strings = gser_string_defs, 11261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 11361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 11461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_gadget_strings *gser_strings[] = { 11561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell &gser_string_table, 11661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell NULL, 11761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 11861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 11961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/ 12061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 12161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 12261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 12361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser = func_to_gser(f); 12461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_composite_dev *cdev = f->config->cdev; 12561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 12661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* we know alt == 0, so this is an activation or a reset */ 12761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 12861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser->port.in->driver_data) { 12961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); 13061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gserial_disconnect(&gser->port); 131ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman } 132ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman if (!gser->port.in->desc || !gser->port.out->desc) { 13361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); 134fef6964452abae55c82e1615ac505b8377df00e9Robert Jarzmik if (config_ep_by_speed(cdev->gadget, f, gser->port.in) || 135fef6964452abae55c82e1615ac505b8377df00e9Robert Jarzmik config_ep_by_speed(cdev->gadget, f, gser->port.out)) { 136ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman gser->port.in->desc = NULL; 137ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman gser->port.out->desc = NULL; 138ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman return -EINVAL; 139ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman } 14061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell } 14161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gserial_connect(&gser->port, gser->port_num); 14261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return 0; 14361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 14461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 14561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic void gser_disable(struct usb_function *f) 14661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 14761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser = func_to_gser(f); 14861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_composite_dev *cdev = f->config->cdev; 14961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 15061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); 15161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gserial_disconnect(&gser->port); 15261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 15361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 15461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/ 15561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 15661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* serial function driver setup/binding */ 15761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 15861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic int __init 15961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellgser_bind(struct usb_configuration *c, struct usb_function *f) 16061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 16161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_composite_dev *cdev = c->cdev; 16261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser = func_to_gser(f); 16361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell int status; 16461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_ep *ep; 16561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 16661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* allocate instance-specific interface IDs */ 16761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = usb_interface_id(c, f); 16861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (status < 0) 16961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell goto fail; 17061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->data_id = status; 17161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_interface_desc.bInterfaceNumber = status; 17261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 17361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = -ENODEV; 17461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 17561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* allocate instance-specific endpoints */ 17661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); 17761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (!ep) 17861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell goto fail; 17961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.in = ep; 18061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep->driver_data = cdev; /* claim */ 18161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 18261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); 18361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (!ep) 18461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell goto fail; 18561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.out = ep; 18661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep->driver_data = cdev; /* claim */ 18761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 18861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* copy descriptors, and track endpoint copies */ 18961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell f->descriptors = usb_copy_descriptors(gser_fs_function); 19061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 19161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* support all relevant hardware speeds... we expect that when 19261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * hardware is dual speed, all bulk-capable endpoints work at 19361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * both speeds 19461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 19561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gadget_is_dualspeed(c->cdev->gadget)) { 19661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_hs_in_desc.bEndpointAddress = 19761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_fs_in_desc.bEndpointAddress; 19861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_hs_out_desc.bEndpointAddress = 19961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_fs_out_desc.bEndpointAddress; 20061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 20161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* copy descriptors, and track endpoint copies */ 20261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell f->hs_descriptors = usb_copy_descriptors(gser_hs_function); 20361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell } 20461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 20561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", 20661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port_num, 20761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", 20861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.in->name, gser->port.out->name); 20961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return 0; 21061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 21161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellfail: 21261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* we might as well release our claims on endpoints */ 21361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser->port.out) 21461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.out->driver_data = NULL; 21561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser->port.in) 21661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.in->driver_data = NULL; 21761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 21861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); 21961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 22061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return status; 22161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 22261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 22361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic void 22461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellgser_unbind(struct usb_configuration *c, struct usb_function *f) 22561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 22661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gadget_is_dualspeed(c->cdev->gadget)) 22761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell usb_free_descriptors(f->hs_descriptors); 22861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell usb_free_descriptors(f->descriptors); 22961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell kfree(func_to_gser(f)); 23061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 23161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 23261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/** 23361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * gser_bind_config - add a generic serial function to a configuration 23461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * @c: the configuration to support the serial instance 23561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * @port_num: /dev/ttyGS* port this interface will use 23661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Context: single threaded during gadget setup 23761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * 23861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Returns zero on success, else negative errno. 23961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * 24061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Caller must have called @gserial_setup() with enough ports to 24161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * handle all the ones it binds. Caller is also responsible 24261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * for calling @gserial_cleanup() before module unload. 24361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 24461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellint __init gser_bind_config(struct usb_configuration *c, u8 port_num) 24561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 24661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser; 24761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell int status; 24861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 24961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* REVISIT might want instance-specific strings to help 25061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * distinguish instances ... 25161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 25261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 25361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* maybe allocate device-global string ID */ 25461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser_string_defs[0].id == 0) { 25561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = usb_string_id(c->cdev); 25661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (status < 0) 25761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return status; 25861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_string_defs[0].id = status; 25961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell } 26061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 26161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* allocate and initialize one new instance */ 26261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser = kzalloc(sizeof *gser, GFP_KERNEL); 26361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (!gser) 26461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return -ENOMEM; 26561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 26661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port_num = port_num; 26761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 26861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.name = "gser"; 26961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.strings = gser_strings; 27061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.bind = gser_bind; 27161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.unbind = gser_unbind; 27261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.set_alt = gser_set_alt; 27361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.disable = gser_disable; 27461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 27561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = usb_add_function(c, &gser->port.func); 27661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (status) 27761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell kfree(gser); 27861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return status; 27961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 280