cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/gadget/function/f_serial.c

/*
 * f_serial.c - generic USB serial function driver
 *
 * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
 * Copyright (C) 2008 by David Brownell
 * Copyright (C) 2008 by Nokia Corporation
 *
 * This software is distributed under the terms of the GNU General
 * Public License ("GPL") as published by the Free Software Foundation,
 * either version 2 of that License or (at your option) any later version.
 */

#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>

#include "u_serial.h"


/*
 * This function packages a simple "generic serial" port with no real
 * control mechanisms, just raw data transfer over two bulk endpoints.
 *
 * Because it's not standardized, this isn't as interoperable as the
 * CDC ACM driver.  However, for many purposes it's just as functional
 * if you can arrange appropriate host side drivers.
 */


struct f_gser {
	
struct gserial			port;
	
u8				data_id;
	
u8				port_num;
};


static inline struct f_gser *func_to_gser(struct usb_function *f) { return container_of(f, struct f_gser, port.func); }

Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell27100.00%1100.00%
Total27100.00%1100.00%

/*-------------------------------------------------------------------------*/ /* interface descriptor: */ static struct usb_interface_descriptor gser_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ .bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, .bInterfaceSubClass = 0, .bInterfaceProtocol = 0, /* .iInterface = DYNAMIC */ }; /* full speed support: */ static struct usb_endpoint_descriptor gser_fs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; static struct usb_endpoint_descriptor gser_fs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, .bmAttributes = USB_ENDPOINT_XFER_BULK, }; static struct usb_descriptor_header *gser_fs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_fs_in_desc, (struct usb_descriptor_header *) &gser_fs_out_desc, NULL, }; /* high speed support: */ static struct usb_endpoint_descriptor gser_hs_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_endpoint_descriptor gser_hs_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(512), }; static struct usb_descriptor_header *gser_hs_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_hs_in_desc, (struct usb_descriptor_header *) &gser_hs_out_desc, NULL, }; static struct usb_endpoint_descriptor gser_ss_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; static struct usb_endpoint_descriptor gser_ss_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = cpu_to_le16(1024), }; static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = { .bLength = sizeof gser_ss_bulk_comp_desc, .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, }; static struct usb_descriptor_header *gser_ss_function[] = { (struct usb_descriptor_header *) &gser_interface_desc, (struct usb_descriptor_header *) &gser_ss_in_desc, (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, (struct usb_descriptor_header *) &gser_ss_out_desc, (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc, NULL, }; /* string descriptors: */ static struct usb_string gser_string_defs[] = { [0].s = "Generic Serial", { } /* end of list */ }; static struct usb_gadget_strings gser_string_table = { .language = 0x0409, /* en-us */ .strings = gser_string_defs, }; static struct usb_gadget_strings *gser_strings[] = { &gser_string_table, NULL, }; /*-------------------------------------------------------------------------*/
static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) { struct f_gser *gser = func_to_gser(f); struct usb_composite_dev *cdev = f->config->cdev; /* we know alt == 0, so this is an activation or a reset */ if (gser->port.in->enabled) { dev_dbg(&cdev->gadget->dev, "reset generic ttyGS%d\n", gser->port_num); gserial_disconnect(&gser->port); } if (!gser->port.in->desc || !gser->port.out->desc) { dev_dbg(&cdev->gadget->dev, "activate generic ttyGS%d\n", gser->port_num); if (config_ep_by_speed(cdev->gadget, f, gser->port.in) || config_ep_by_speed(cdev->gadget, f, gser->port.out)) { gser->port.in->desc = NULL; gser->port.out->desc = NULL; return -EINVAL; } } gserial_connect(&gser->port, gser->port_num); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell12064.52%120.00%
tatyana brokhmantatyana brokhman5328.49%240.00%
richard leitnerrichard leitner126.45%120.00%
robert baldygarobert baldyga10.54%120.00%
Total186100.00%5100.00%


static void gser_disable(struct usb_function *f) { struct f_gser *gser = func_to_gser(f); struct usb_composite_dev *cdev = f->config->cdev; dev_dbg(&cdev->gadget->dev, "generic ttyGS%d deactivated\n", gser->port_num); gserial_disconnect(&gser->port); }

Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell5089.29%150.00%
richard leitnerrichard leitner610.71%150.00%
Total56100.00%2100.00%

/*-------------------------------------------------------------------------*/ /* serial function driver setup/binding */
static int gser_bind(struct usb_configuration *c, struct usb_function *f) { struct usb_composite_dev *cdev = c->cdev; struct f_gser *gser = func_to_gser(f); int status; struct usb_ep *ep; /* REVISIT might want instance-specific strings to help * distinguish instances ... */ /* maybe allocate device-global string ID */ if (gser_string_defs[0].id == 0) { status = usb_string_id(c->cdev); if (status < 0) return status; gser_string_defs[0].id = status; } /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) goto fail; gser->data_id = status; gser_interface_desc.bInterfaceNumber = status; status = -ENODEV; /* allocate instance-specific endpoints */ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc); if (!ep) goto fail; gser->port.in = ep; ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); if (!ep) goto fail; gser->port.out = ep; /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at * both speeds */ gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress; gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress; gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress; status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function, gser_ss_function, NULL); if (status) goto fail; dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n", gser->port_num, gadget_is_superspeed(c->cdev->gadget) ? "super" : gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", gser->port.in->name, gser->port.out->name); return 0; fail: ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; }

Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell21368.71%116.67%
sebastian andrzej siewiorsebastian andrzej siewior4715.16%233.33%
andrzej pietrasiewiczandrzej pietrasiewicz4213.55%116.67%
richard leitnerrichard leitner61.94%116.67%
john younjohn youn20.65%116.67%
Total310100.00%6100.00%


static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) { return container_of(to_config_group(item), struct f_serial_opts, func_inst.group); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz30100.00%1100.00%
Total30100.00%1100.00%


static void serial_attr_release(struct config_item *item) { struct f_serial_opts *opts = to_f_serial_opts(item); usb_put_function_instance(&opts->func_inst); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz29100.00%1100.00%
Total29100.00%1100.00%

static struct configfs_item_operations serial_item_ops = { .release = serial_attr_release, };
static ssize_t f_serial_port_num_show(struct config_item *item, char *page) { return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz2480.00%150.00%
christoph hellwigchristoph hellwig620.00%150.00%
Total30100.00%2100.00%

CONFIGFS_ATTR_RO(f_serial_, port_num); static struct configfs_attribute *acm_attrs[] = { &f_serial_attr_port_num, NULL, }; static struct config_item_type serial_func_type = { .ct_item_ops = &serial_item_ops, .ct_attrs = acm_attrs, .ct_owner = THIS_MODULE, };
static void gser_free_inst(struct usb_function_instance *f) { struct f_serial_opts *opts; opts = container_of(f, struct f_serial_opts, func_inst); gserial_free_line(opts->port_num); kfree(opts); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz40100.00%1100.00%
Total40100.00%1100.00%


static struct usb_function_instance *gser_alloc_inst(void) { struct f_serial_opts *opts; int ret; opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return ERR_PTR(-ENOMEM); opts->func_inst.free_func_inst = gser_free_inst; ret = gserial_alloc_line(&opts->port_num); if (ret) { kfree(opts); return ERR_PTR(ret); } config_group_init_type_name(&opts->func_inst.group, "", &serial_func_type); return &opts->func_inst; }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz9797.98%266.67%
david brownelldavid brownell22.02%133.33%
Total99100.00%3100.00%


static void gser_free(struct usb_function *f) { struct f_gser *serial; serial = func_to_gser(f); kfree(serial); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz28100.00%1100.00%
Total28100.00%1100.00%


static void gser_unbind(struct usb_configuration *c, struct usb_function *f) { usb_free_all_descriptors(f); }

Contributors

PersonTokensPropCommitsCommitProp
andrzej pietrasiewiczandrzej pietrasiewicz1990.48%150.00%
david brownelldavid brownell29.52%150.00%
Total21100.00%2100.00%


static struct usb_function *gser_alloc(struct usb_function_instance *fi) { struct f_gser *gser; struct f_serial_opts *opts; /* allocate and initialize one new instance */ gser = kzalloc(sizeof(*gser), GFP_KERNEL); if (!gser) return ERR_PTR(-ENOMEM); opts = container_of(fi, struct f_serial_opts, func_inst); gser->port_num = opts->port_num; gser->port.func.name = "gser"; gser->port.func.strings = gser_strings; gser->port.func.bind = gser_bind; gser->port.func.unbind = gser_unbind; gser->port.func.set_alt = gser_set_alt; gser->port.func.disable = gser_disable; gser->port.func.free_func = gser_free; return &gser->port.func; }

Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell9363.27%133.33%
andrzej pietrasiewiczandrzej pietrasiewicz5336.05%133.33%
jingoo hanjingoo han10.68%133.33%
Total147100.00%3100.00%

DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Al Borchers"); MODULE_AUTHOR("David Brownell");

Overall Contributors

PersonTokensPropCommitsCommitProp
david brownelldavid brownell81953.63%17.14%
andrzej pietrasiewiczandrzej pietrasiewicz43528.49%214.29%
sebastian andrzej siewiorsebastian andrzej siewior17611.53%214.29%
tatyana brokhmantatyana brokhman533.47%214.29%
richard leitnerrichard leitner241.57%17.14%
christoph hellwigchristoph hellwig110.72%17.14%
tejun heotejun heo30.20%17.14%
john younjohn youn20.13%17.14%
harvey harrisonharvey harrison20.13%17.14%
robert baldygarobert baldyga10.07%17.14%
jingoo hanjingoo han10.07%17.14%
Total1527100.00%14100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}