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 1026fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_endpoint_descriptor gser_ss_in_desc __initdata = { 1036fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bLength = USB_DT_ENDPOINT_SIZE, 1046fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bDescriptorType = USB_DT_ENDPOINT, 1056fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bmAttributes = USB_ENDPOINT_XFER_BULK, 1066fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .wMaxPacketSize = cpu_to_le16(1024), 1076fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior}; 1086fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior 1096fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_endpoint_descriptor gser_ss_out_desc __initdata = { 1106fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bLength = USB_DT_ENDPOINT_SIZE, 1116fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bDescriptorType = USB_DT_ENDPOINT, 1126fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bmAttributes = USB_ENDPOINT_XFER_BULK, 1136fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .wMaxPacketSize = cpu_to_le16(1024), 1146fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior}; 1156fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior 1166fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc __initdata = { 1176fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bLength = sizeof gser_ss_bulk_comp_desc, 1186fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 1196fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior}; 1206fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior 1216fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewiorstatic struct usb_descriptor_header *gser_ss_function[] __initdata = { 1226fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior (struct usb_descriptor_header *) &gser_interface_desc, 1236fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior (struct usb_descriptor_header *) &gser_ss_in_desc, 1246fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, 1256fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior (struct usb_descriptor_header *) &gser_ss_out_desc, 1266fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, 1276fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior NULL, 1286fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior}; 1296fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior 13061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* string descriptors: */ 13161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 13261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_string gser_string_defs[] = { 13361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell [0].s = "Generic Serial", 13461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell { } /* end of list */ 13561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 13661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 13761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_gadget_strings gser_string_table = { 13861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .language = 0x0409, /* en-us */ 13961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell .strings = gser_string_defs, 14061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 14161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 14261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic struct usb_gadget_strings *gser_strings[] = { 14361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell &gser_string_table, 14461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell NULL, 14561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell}; 14661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 14761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/ 14861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 14961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 15061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 15161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser = func_to_gser(f); 15261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_composite_dev *cdev = f->config->cdev; 15361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 15461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* we know alt == 0, so this is an activation or a reset */ 15561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 15661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser->port.in->driver_data) { 15761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "reset generic ttyGS%d\n", gser->port_num); 15861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gserial_disconnect(&gser->port); 159ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman } 160ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman if (!gser->port.in->desc || !gser->port.out->desc) { 16161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "activate generic ttyGS%d\n", gser->port_num); 162fef6964452abae55c82e1615ac505b8377df00e9Robert Jarzmik if (config_ep_by_speed(cdev->gadget, f, gser->port.in) || 163fef6964452abae55c82e1615ac505b8377df00e9Robert Jarzmik config_ep_by_speed(cdev->gadget, f, gser->port.out)) { 164ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman gser->port.in->desc = NULL; 165ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman gser->port.out->desc = NULL; 166ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman return -EINVAL; 167ea2a1df7b2b1de839a72217d85bfb4b7b049010cTatyana Brokhman } 16861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell } 16961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gserial_connect(&gser->port, gser->port_num); 17061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return 0; 17161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 17261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 17361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic void gser_disable(struct usb_function *f) 17461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 17561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser = func_to_gser(f); 17661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_composite_dev *cdev = f->config->cdev; 17761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 17861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num); 17961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gserial_disconnect(&gser->port); 18061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 18161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 18261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/*-------------------------------------------------------------------------*/ 18361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 18461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/* serial function driver setup/binding */ 18561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 18661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic int __init 18761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellgser_bind(struct usb_configuration *c, struct usb_function *f) 18861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 18961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_composite_dev *cdev = c->cdev; 19061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser = func_to_gser(f); 19161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell int status; 19261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct usb_ep *ep; 19361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 19461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* allocate instance-specific interface IDs */ 19561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = usb_interface_id(c, f); 19661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (status < 0) 19761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell goto fail; 19861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->data_id = status; 19961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_interface_desc.bInterfaceNumber = status; 20061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 20161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = -ENODEV; 20261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 20361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* allocate instance-specific endpoints */ 20461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); 20561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (!ep) 20661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell goto fail; 20761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.in = ep; 20861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep->driver_data = cdev; /* claim */ 20961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 21061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); 21161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (!ep) 21261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell goto fail; 21361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.out = ep; 21461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ep->driver_data = cdev; /* claim */ 21561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 21661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* copy descriptors, and track endpoint copies */ 21761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell f->descriptors = usb_copy_descriptors(gser_fs_function); 21861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 21961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* support all relevant hardware speeds... we expect that when 22061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * hardware is dual speed, all bulk-capable endpoints work at 22161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * both speeds 22261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 22361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gadget_is_dualspeed(c->cdev->gadget)) { 22461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_hs_in_desc.bEndpointAddress = 22561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_fs_in_desc.bEndpointAddress; 22661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_hs_out_desc.bEndpointAddress = 22761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_fs_out_desc.bEndpointAddress; 22861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 22961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* copy descriptors, and track endpoint copies */ 23061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell f->hs_descriptors = usb_copy_descriptors(gser_hs_function); 23161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell } 2326fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior if (gadget_is_superspeed(c->cdev->gadget)) { 2336fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior gser_ss_in_desc.bEndpointAddress = 2346fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior gser_fs_in_desc.bEndpointAddress; 2356fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior gser_ss_out_desc.bEndpointAddress = 2366fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior gser_fs_out_desc.bEndpointAddress; 2376fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior 2386fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior /* copy descriptors, and track endpoint copies */ 2396fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior f->ss_descriptors = usb_copy_descriptors(gser_ss_function); 2406fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior if (!f->ss_descriptors) 2416fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior goto fail; 2426fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior } 24361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 24461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", 24561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port_num, 2466fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior gadget_is_superspeed(c->cdev->gadget) ? "super" : 24761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", 24861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.in->name, gser->port.out->name); 24961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return 0; 25061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 25161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellfail: 25261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* we might as well release our claims on endpoints */ 25361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser->port.out) 25461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.out->driver_data = NULL; 25561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser->port.in) 25661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.in->driver_data = NULL; 25761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 25861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); 25961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 26061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return status; 26161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 26261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 26361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellstatic void 26461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellgser_unbind(struct usb_configuration *c, struct usb_function *f) 26561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 26661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gadget_is_dualspeed(c->cdev->gadget)) 26761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell usb_free_descriptors(f->hs_descriptors); 2686fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior if (gadget_is_superspeed(c->cdev->gadget)) 2696fecfb05c077586ba85aa6f52f10abf30a4bfb40Sebastian Andrzej Siewior usb_free_descriptors(f->ss_descriptors); 27061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell usb_free_descriptors(f->descriptors); 27161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell kfree(func_to_gser(f)); 27261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 27361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 27461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell/** 27561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * gser_bind_config - add a generic serial function to a configuration 27661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * @c: the configuration to support the serial instance 27761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * @port_num: /dev/ttyGS* port this interface will use 27861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Context: single threaded during gadget setup 27961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * 28061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Returns zero on success, else negative errno. 28161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * 28261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * Caller must have called @gserial_setup() with enough ports to 28361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * handle all the ones it binds. Caller is also responsible 28461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * for calling @gserial_cleanup() before module unload. 28561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 28661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownellint __init gser_bind_config(struct usb_configuration *c, u8 port_num) 28761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell{ 28861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell struct f_gser *gser; 28961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell int status; 29061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 29161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* REVISIT might want instance-specific strings to help 29261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell * distinguish instances ... 29361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell */ 29461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 29561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* maybe allocate device-global string ID */ 29661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (gser_string_defs[0].id == 0) { 29761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = usb_string_id(c->cdev); 29861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (status < 0) 29961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return status; 30061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser_string_defs[0].id = status; 30161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell } 30261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 30361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell /* allocate and initialize one new instance */ 30461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser = kzalloc(sizeof *gser, GFP_KERNEL); 30561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (!gser) 30661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return -ENOMEM; 30761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 30861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port_num = port_num; 30961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 31061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.name = "gser"; 31161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.strings = gser_strings; 31261d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.bind = gser_bind; 31361d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.unbind = gser_unbind; 31461d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.set_alt = gser_set_alt; 31561d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell gser->port.func.disable = gser_disable; 31661d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell 31761d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell status = usb_add_function(c, &gser->port.func); 31861d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell if (status) 31961d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell kfree(gser); 32061d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell return status; 32161d8baea5d02f0f00fb789ce5551cbd8f8b77087David Brownell} 322