diff options
Diffstat (limited to 'drivers/usb/gadget/f_serial.c')
-rw-r--r-- | drivers/usb/gadget/f_serial.c | 174 |
1 files changed, 130 insertions, 44 deletions
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c index da33cfb3031d..981113c9924d 100644 --- a/drivers/usb/gadget/f_serial.c +++ b/drivers/usb/gadget/f_serial.c @@ -12,6 +12,7 @@ #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/device.h> #include "u_serial.h" @@ -42,7 +43,7 @@ static inline struct f_gser *func_to_gser(struct usb_function *f) /* interface descriptor: */ -static struct usb_interface_descriptor gser_interface_desc __initdata = { +static struct usb_interface_descriptor gser_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, /* .bInterfaceNumber = DYNAMIC */ @@ -55,21 +56,21 @@ static struct usb_interface_descriptor gser_interface_desc __initdata = { /* full speed support: */ -static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = { +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 __initdata = { +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[] __initdata = { +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, @@ -78,47 +79,47 @@ static struct usb_descriptor_header *gser_fs_function[] __initdata = { /* high speed support: */ -static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = { +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 __initdata = { +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[] __initdata = { +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 __initdata = { +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 __initdata = { +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 __initdata = { +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[] __initdata = { +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, @@ -183,14 +184,25 @@ static void gser_disable(struct usb_function *f) /* serial function driver setup/binding */ -static int __init -gser_bind(struct usb_configuration *c, struct usb_function *f) +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) @@ -246,44 +258,115 @@ fail: return status; } -static void -gser_unbind(struct usb_configuration *c, struct usb_function *f) +static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) { - usb_free_all_descriptors(f); - kfree(func_to_gser(f)); + return container_of(to_config_group(item), struct f_serial_opts, + func_inst.group); } -/** - * gser_bind_config - add a generic serial function to a configuration - * @c: the configuration to support the serial instance - * @port_num: /dev/ttyGS* port this interface will use - * Context: single threaded during gadget setup - * - * Returns zero on success, else negative errno. - */ -int __init gser_bind_config(struct usb_configuration *c, u8 port_num) +CONFIGFS_ATTR_STRUCT(f_serial_opts); +static ssize_t f_serial_attr_show(struct config_item *item, + struct configfs_attribute *attr, + char *page) { - struct f_gser *gser; - int status; + struct f_serial_opts *opts = to_f_serial_opts(item); + struct f_serial_opts_attribute *f_serial_opts_attr = + container_of(attr, struct f_serial_opts_attribute, attr); + ssize_t ret = 0; - /* REVISIT might want instance-specific strings to help - * distinguish instances ... - */ + if (f_serial_opts_attr->show) + ret = f_serial_opts_attr->show(opts, page); - /* 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; + return ret; +} + +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); +} + +static struct configfs_item_operations serial_item_ops = { + .release = serial_attr_release, + .show_attribute = f_serial_attr_show, +}; + +static ssize_t f_serial_port_num_show(struct f_serial_opts *opts, char *page) +{ + return sprintf(page, "%u\n", opts->port_num); +} + +static struct f_serial_opts_attribute f_serial_port_num = + __CONFIGFS_ATTR_RO(port_num, f_serial_port_num_show); + +static struct configfs_attribute *acm_attrs[] = { + &f_serial_port_num.attr, + 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); +} + +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; +} + +static void gser_free(struct usb_function *f) +{ + struct f_gser *serial; + + serial = func_to_gser(f); + kfree(serial); +} + +static void gser_unbind(struct usb_configuration *c, struct usb_function *f) +{ + usb_free_all_descriptors(f); +} + +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); + gser = kzalloc(sizeof(*gser), GFP_KERNEL); if (!gser) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - gser->port_num = port_num; + 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; @@ -291,9 +374,12 @@ int __init gser_bind_config(struct usb_configuration *c, u8 port_num) 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; - status = usb_add_function(c, &gser->port.func); - if (status) - kfree(gser); - return status; + return &gser->port.func; } + +DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Al Borchers"); +MODULE_AUTHOR("David Brownell"); |