15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
200a2430fSAndrzej Pietrasiewicz /*
300a2430fSAndrzej Pietrasiewicz * f_serial.c - generic USB serial function driver
400a2430fSAndrzej Pietrasiewicz *
500a2430fSAndrzej Pietrasiewicz * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
600a2430fSAndrzej Pietrasiewicz * Copyright (C) 2008 by David Brownell
700a2430fSAndrzej Pietrasiewicz * Copyright (C) 2008 by Nokia Corporation
800a2430fSAndrzej Pietrasiewicz */
900a2430fSAndrzej Pietrasiewicz
1000a2430fSAndrzej Pietrasiewicz #include <linux/slab.h>
1100a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h>
1200a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
1300a2430fSAndrzej Pietrasiewicz #include <linux/device.h>
1400a2430fSAndrzej Pietrasiewicz
1500a2430fSAndrzej Pietrasiewicz #include "u_serial.h"
1600a2430fSAndrzej Pietrasiewicz
1700a2430fSAndrzej Pietrasiewicz
1800a2430fSAndrzej Pietrasiewicz /*
1900a2430fSAndrzej Pietrasiewicz * This function packages a simple "generic serial" port with no real
2000a2430fSAndrzej Pietrasiewicz * control mechanisms, just raw data transfer over two bulk endpoints.
2100a2430fSAndrzej Pietrasiewicz *
2200a2430fSAndrzej Pietrasiewicz * Because it's not standardized, this isn't as interoperable as the
2300a2430fSAndrzej Pietrasiewicz * CDC ACM driver. However, for many purposes it's just as functional
2400a2430fSAndrzej Pietrasiewicz * if you can arrange appropriate host side drivers.
2500a2430fSAndrzej Pietrasiewicz */
2600a2430fSAndrzej Pietrasiewicz
2700a2430fSAndrzej Pietrasiewicz struct f_gser {
2800a2430fSAndrzej Pietrasiewicz struct gserial port;
2900a2430fSAndrzej Pietrasiewicz u8 data_id;
3000a2430fSAndrzej Pietrasiewicz u8 port_num;
3100a2430fSAndrzej Pietrasiewicz };
3200a2430fSAndrzej Pietrasiewicz
func_to_gser(struct usb_function * f)3300a2430fSAndrzej Pietrasiewicz static inline struct f_gser *func_to_gser(struct usb_function *f)
3400a2430fSAndrzej Pietrasiewicz {
3500a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_gser, port.func);
3600a2430fSAndrzej Pietrasiewicz }
3700a2430fSAndrzej Pietrasiewicz
3800a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3900a2430fSAndrzej Pietrasiewicz
4000a2430fSAndrzej Pietrasiewicz /* interface descriptor: */
4100a2430fSAndrzej Pietrasiewicz
4200a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor gser_interface_desc = {
4300a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_INTERFACE_SIZE,
4400a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE,
4500a2430fSAndrzej Pietrasiewicz /* .bInterfaceNumber = DYNAMIC */
4600a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2,
4700a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
4800a2430fSAndrzej Pietrasiewicz .bInterfaceSubClass = 0,
4900a2430fSAndrzej Pietrasiewicz .bInterfaceProtocol = 0,
5000a2430fSAndrzej Pietrasiewicz /* .iInterface = DYNAMIC */
5100a2430fSAndrzej Pietrasiewicz };
5200a2430fSAndrzej Pietrasiewicz
5300a2430fSAndrzej Pietrasiewicz /* full speed support: */
5400a2430fSAndrzej Pietrasiewicz
5500a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor gser_fs_in_desc = {
5600a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
5700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
5800a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN,
5900a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
6000a2430fSAndrzej Pietrasiewicz };
6100a2430fSAndrzej Pietrasiewicz
6200a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor gser_fs_out_desc = {
6300a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
6400a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
6500a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT,
6600a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
6700a2430fSAndrzej Pietrasiewicz };
6800a2430fSAndrzej Pietrasiewicz
6900a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *gser_fs_function[] = {
7000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_interface_desc,
7100a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_fs_in_desc,
7200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_fs_out_desc,
7300a2430fSAndrzej Pietrasiewicz NULL,
7400a2430fSAndrzej Pietrasiewicz };
7500a2430fSAndrzej Pietrasiewicz
7600a2430fSAndrzej Pietrasiewicz /* high speed support: */
7700a2430fSAndrzej Pietrasiewicz
7800a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor gser_hs_in_desc = {
7900a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
8000a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
8100a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
8200a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512),
8300a2430fSAndrzej Pietrasiewicz };
8400a2430fSAndrzej Pietrasiewicz
8500a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor gser_hs_out_desc = {
8600a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
8700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
8800a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
8900a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512),
9000a2430fSAndrzej Pietrasiewicz };
9100a2430fSAndrzej Pietrasiewicz
9200a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *gser_hs_function[] = {
9300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_interface_desc,
9400a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_hs_in_desc,
9500a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_hs_out_desc,
9600a2430fSAndrzej Pietrasiewicz NULL,
9700a2430fSAndrzej Pietrasiewicz };
9800a2430fSAndrzej Pietrasiewicz
9900a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor gser_ss_in_desc = {
10000a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
10100a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
10200a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
10300a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024),
10400a2430fSAndrzej Pietrasiewicz };
10500a2430fSAndrzej Pietrasiewicz
10600a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor gser_ss_out_desc = {
10700a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
10800a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
10900a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
11000a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(1024),
11100a2430fSAndrzej Pietrasiewicz };
11200a2430fSAndrzej Pietrasiewicz
11300a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor gser_ss_bulk_comp_desc = {
11400a2430fSAndrzej Pietrasiewicz .bLength = sizeof gser_ss_bulk_comp_desc,
11500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
11600a2430fSAndrzej Pietrasiewicz };
11700a2430fSAndrzej Pietrasiewicz
11800a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *gser_ss_function[] = {
11900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_interface_desc,
12000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_ss_in_desc,
12100a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
12200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_ss_out_desc,
12300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &gser_ss_bulk_comp_desc,
12400a2430fSAndrzej Pietrasiewicz NULL,
12500a2430fSAndrzej Pietrasiewicz };
12600a2430fSAndrzej Pietrasiewicz
12700a2430fSAndrzej Pietrasiewicz /* string descriptors: */
12800a2430fSAndrzej Pietrasiewicz
12900a2430fSAndrzej Pietrasiewicz static struct usb_string gser_string_defs[] = {
13000a2430fSAndrzej Pietrasiewicz [0].s = "Generic Serial",
13100a2430fSAndrzej Pietrasiewicz { } /* end of list */
13200a2430fSAndrzej Pietrasiewicz };
13300a2430fSAndrzej Pietrasiewicz
13400a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings gser_string_table = {
13500a2430fSAndrzej Pietrasiewicz .language = 0x0409, /* en-us */
13600a2430fSAndrzej Pietrasiewicz .strings = gser_string_defs,
13700a2430fSAndrzej Pietrasiewicz };
13800a2430fSAndrzej Pietrasiewicz
13900a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *gser_strings[] = {
14000a2430fSAndrzej Pietrasiewicz &gser_string_table,
14100a2430fSAndrzej Pietrasiewicz NULL,
14200a2430fSAndrzej Pietrasiewicz };
14300a2430fSAndrzej Pietrasiewicz
14400a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
14500a2430fSAndrzej Pietrasiewicz
gser_set_alt(struct usb_function * f,unsigned intf,unsigned alt)14600a2430fSAndrzej Pietrasiewicz static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
14700a2430fSAndrzej Pietrasiewicz {
14800a2430fSAndrzej Pietrasiewicz struct f_gser *gser = func_to_gser(f);
14900a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev;
15000a2430fSAndrzej Pietrasiewicz
15100a2430fSAndrzej Pietrasiewicz /* we know alt == 0, so this is an activation or a reset */
15200a2430fSAndrzej Pietrasiewicz
153885d22ebSRobert Baldyga if (gser->port.in->enabled) {
154b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
155b8b0ea51SRichard Leitner "reset generic ttyGS%d\n", gser->port_num);
15600a2430fSAndrzej Pietrasiewicz gserial_disconnect(&gser->port);
15700a2430fSAndrzej Pietrasiewicz }
15800a2430fSAndrzej Pietrasiewicz if (!gser->port.in->desc || !gser->port.out->desc) {
159b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
160b8b0ea51SRichard Leitner "activate generic ttyGS%d\n", gser->port_num);
16100a2430fSAndrzej Pietrasiewicz if (config_ep_by_speed(cdev->gadget, f, gser->port.in) ||
16200a2430fSAndrzej Pietrasiewicz config_ep_by_speed(cdev->gadget, f, gser->port.out)) {
16300a2430fSAndrzej Pietrasiewicz gser->port.in->desc = NULL;
16400a2430fSAndrzej Pietrasiewicz gser->port.out->desc = NULL;
16500a2430fSAndrzej Pietrasiewicz return -EINVAL;
16600a2430fSAndrzej Pietrasiewicz }
16700a2430fSAndrzej Pietrasiewicz }
16800a2430fSAndrzej Pietrasiewicz gserial_connect(&gser->port, gser->port_num);
16900a2430fSAndrzej Pietrasiewicz return 0;
17000a2430fSAndrzej Pietrasiewicz }
17100a2430fSAndrzej Pietrasiewicz
gser_disable(struct usb_function * f)17200a2430fSAndrzej Pietrasiewicz static void gser_disable(struct usb_function *f)
17300a2430fSAndrzej Pietrasiewicz {
17400a2430fSAndrzej Pietrasiewicz struct f_gser *gser = func_to_gser(f);
17500a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = f->config->cdev;
17600a2430fSAndrzej Pietrasiewicz
177b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
178b8b0ea51SRichard Leitner "generic ttyGS%d deactivated\n", gser->port_num);
17900a2430fSAndrzej Pietrasiewicz gserial_disconnect(&gser->port);
18000a2430fSAndrzej Pietrasiewicz }
18100a2430fSAndrzej Pietrasiewicz
18200a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
18300a2430fSAndrzej Pietrasiewicz
18400a2430fSAndrzej Pietrasiewicz /* serial function driver setup/binding */
18500a2430fSAndrzej Pietrasiewicz
gser_bind(struct usb_configuration * c,struct usb_function * f)18600a2430fSAndrzej Pietrasiewicz static int gser_bind(struct usb_configuration *c, struct usb_function *f)
18700a2430fSAndrzej Pietrasiewicz {
18800a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = c->cdev;
18900a2430fSAndrzej Pietrasiewicz struct f_gser *gser = func_to_gser(f);
19000a2430fSAndrzej Pietrasiewicz int status;
19100a2430fSAndrzej Pietrasiewicz struct usb_ep *ep;
19200a2430fSAndrzej Pietrasiewicz
19300a2430fSAndrzej Pietrasiewicz /* REVISIT might want instance-specific strings to help
19400a2430fSAndrzej Pietrasiewicz * distinguish instances ...
19500a2430fSAndrzej Pietrasiewicz */
19600a2430fSAndrzej Pietrasiewicz
19700a2430fSAndrzej Pietrasiewicz /* maybe allocate device-global string ID */
19800a2430fSAndrzej Pietrasiewicz if (gser_string_defs[0].id == 0) {
19900a2430fSAndrzej Pietrasiewicz status = usb_string_id(c->cdev);
20000a2430fSAndrzej Pietrasiewicz if (status < 0)
20100a2430fSAndrzej Pietrasiewicz return status;
20200a2430fSAndrzej Pietrasiewicz gser_string_defs[0].id = status;
20300a2430fSAndrzej Pietrasiewicz }
20400a2430fSAndrzej Pietrasiewicz
20500a2430fSAndrzej Pietrasiewicz /* allocate instance-specific interface IDs */
20600a2430fSAndrzej Pietrasiewicz status = usb_interface_id(c, f);
20700a2430fSAndrzej Pietrasiewicz if (status < 0)
20800a2430fSAndrzej Pietrasiewicz goto fail;
20900a2430fSAndrzej Pietrasiewicz gser->data_id = status;
21000a2430fSAndrzej Pietrasiewicz gser_interface_desc.bInterfaceNumber = status;
21100a2430fSAndrzej Pietrasiewicz
21200a2430fSAndrzej Pietrasiewicz status = -ENODEV;
21300a2430fSAndrzej Pietrasiewicz
21400a2430fSAndrzej Pietrasiewicz /* allocate instance-specific endpoints */
21500a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
21600a2430fSAndrzej Pietrasiewicz if (!ep)
21700a2430fSAndrzej Pietrasiewicz goto fail;
21800a2430fSAndrzej Pietrasiewicz gser->port.in = ep;
21900a2430fSAndrzej Pietrasiewicz
22000a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
22100a2430fSAndrzej Pietrasiewicz if (!ep)
22200a2430fSAndrzej Pietrasiewicz goto fail;
22300a2430fSAndrzej Pietrasiewicz gser->port.out = ep;
22400a2430fSAndrzej Pietrasiewicz
22500a2430fSAndrzej Pietrasiewicz /* support all relevant hardware speeds... we expect that when
22600a2430fSAndrzej Pietrasiewicz * hardware is dual speed, all bulk-capable endpoints work at
22700a2430fSAndrzej Pietrasiewicz * both speeds
22800a2430fSAndrzej Pietrasiewicz */
22900a2430fSAndrzej Pietrasiewicz gser_hs_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
23000a2430fSAndrzej Pietrasiewicz gser_hs_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
23100a2430fSAndrzej Pietrasiewicz
23200a2430fSAndrzej Pietrasiewicz gser_ss_in_desc.bEndpointAddress = gser_fs_in_desc.bEndpointAddress;
23300a2430fSAndrzej Pietrasiewicz gser_ss_out_desc.bEndpointAddress = gser_fs_out_desc.bEndpointAddress;
23400a2430fSAndrzej Pietrasiewicz
23500a2430fSAndrzej Pietrasiewicz status = usb_assign_descriptors(f, gser_fs_function, gser_hs_function,
23690c4d057SMaciej Żenczykowski gser_ss_function, gser_ss_function);
23700a2430fSAndrzej Pietrasiewicz if (status)
23800a2430fSAndrzej Pietrasiewicz goto fail;
239*333ab99eSLinyu Yuan dev_dbg(&cdev->gadget->dev, "generic ttyGS%d: IN/%s OUT/%s\n",
24000a2430fSAndrzej Pietrasiewicz gser->port_num,
24100a2430fSAndrzej Pietrasiewicz gser->port.in->name, gser->port.out->name);
24200a2430fSAndrzej Pietrasiewicz return 0;
24300a2430fSAndrzej Pietrasiewicz
24400a2430fSAndrzej Pietrasiewicz fail:
24500a2430fSAndrzej Pietrasiewicz ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
24600a2430fSAndrzej Pietrasiewicz
24700a2430fSAndrzej Pietrasiewicz return status;
24800a2430fSAndrzej Pietrasiewicz }
24900a2430fSAndrzej Pietrasiewicz
to_f_serial_opts(struct config_item * item)25000a2430fSAndrzej Pietrasiewicz static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
25100a2430fSAndrzej Pietrasiewicz {
25200a2430fSAndrzej Pietrasiewicz return container_of(to_config_group(item), struct f_serial_opts,
25300a2430fSAndrzej Pietrasiewicz func_inst.group);
25400a2430fSAndrzej Pietrasiewicz }
25500a2430fSAndrzej Pietrasiewicz
serial_attr_release(struct config_item * item)25600a2430fSAndrzej Pietrasiewicz static void serial_attr_release(struct config_item *item)
25700a2430fSAndrzej Pietrasiewicz {
25800a2430fSAndrzej Pietrasiewicz struct f_serial_opts *opts = to_f_serial_opts(item);
25900a2430fSAndrzej Pietrasiewicz
26000a2430fSAndrzej Pietrasiewicz usb_put_function_instance(&opts->func_inst);
26100a2430fSAndrzej Pietrasiewicz }
26200a2430fSAndrzej Pietrasiewicz
26300a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations serial_item_ops = {
26400a2430fSAndrzej Pietrasiewicz .release = serial_attr_release,
26500a2430fSAndrzej Pietrasiewicz };
26600a2430fSAndrzej Pietrasiewicz
267d7cb8fb7SMichał Mirosław #ifdef CONFIG_U_SERIAL_CONSOLE
268d7cb8fb7SMichał Mirosław
f_serial_console_store(struct config_item * item,const char * page,size_t count)269d7cb8fb7SMichał Mirosław static ssize_t f_serial_console_store(struct config_item *item,
270d7cb8fb7SMichał Mirosław const char *page, size_t count)
271d7cb8fb7SMichał Mirosław {
272d7cb8fb7SMichał Mirosław return gserial_set_console(to_f_serial_opts(item)->port_num,
273d7cb8fb7SMichał Mirosław page, count);
274d7cb8fb7SMichał Mirosław }
275d7cb8fb7SMichał Mirosław
f_serial_console_show(struct config_item * item,char * page)276d7cb8fb7SMichał Mirosław static ssize_t f_serial_console_show(struct config_item *item, char *page)
277d7cb8fb7SMichał Mirosław {
278d7cb8fb7SMichał Mirosław return gserial_get_console(to_f_serial_opts(item)->port_num, page);
279d7cb8fb7SMichał Mirosław }
280d7cb8fb7SMichał Mirosław
281d7cb8fb7SMichał Mirosław CONFIGFS_ATTR(f_serial_, console);
282d7cb8fb7SMichał Mirosław
283d7cb8fb7SMichał Mirosław #endif /* CONFIG_U_SERIAL_CONSOLE */
284d7cb8fb7SMichał Mirosław
f_serial_port_num_show(struct config_item * item,char * page)2850b4be4faSChristoph Hellwig static ssize_t f_serial_port_num_show(struct config_item *item, char *page)
28600a2430fSAndrzej Pietrasiewicz {
2870b4be4faSChristoph Hellwig return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
28800a2430fSAndrzej Pietrasiewicz }
28900a2430fSAndrzej Pietrasiewicz
2900b4be4faSChristoph Hellwig CONFIGFS_ATTR_RO(f_serial_, port_num);
29100a2430fSAndrzej Pietrasiewicz
29200a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *acm_attrs[] = {
293d7cb8fb7SMichał Mirosław #ifdef CONFIG_U_SERIAL_CONSOLE
294d7cb8fb7SMichał Mirosław &f_serial_attr_console,
295d7cb8fb7SMichał Mirosław #endif
2960b4be4faSChristoph Hellwig &f_serial_attr_port_num,
29700a2430fSAndrzej Pietrasiewicz NULL,
29800a2430fSAndrzej Pietrasiewicz };
29900a2430fSAndrzej Pietrasiewicz
30097363902SBhumika Goyal static const struct config_item_type serial_func_type = {
30100a2430fSAndrzej Pietrasiewicz .ct_item_ops = &serial_item_ops,
30200a2430fSAndrzej Pietrasiewicz .ct_attrs = acm_attrs,
30300a2430fSAndrzej Pietrasiewicz .ct_owner = THIS_MODULE,
30400a2430fSAndrzej Pietrasiewicz };
30500a2430fSAndrzej Pietrasiewicz
gser_free_inst(struct usb_function_instance * f)30600a2430fSAndrzej Pietrasiewicz static void gser_free_inst(struct usb_function_instance *f)
30700a2430fSAndrzej Pietrasiewicz {
30800a2430fSAndrzej Pietrasiewicz struct f_serial_opts *opts;
30900a2430fSAndrzej Pietrasiewicz
31000a2430fSAndrzej Pietrasiewicz opts = container_of(f, struct f_serial_opts, func_inst);
31100a2430fSAndrzej Pietrasiewicz gserial_free_line(opts->port_num);
31200a2430fSAndrzej Pietrasiewicz kfree(opts);
31300a2430fSAndrzej Pietrasiewicz }
31400a2430fSAndrzej Pietrasiewicz
gser_alloc_inst(void)31500a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *gser_alloc_inst(void)
31600a2430fSAndrzej Pietrasiewicz {
31700a2430fSAndrzej Pietrasiewicz struct f_serial_opts *opts;
31800a2430fSAndrzej Pietrasiewicz int ret;
31900a2430fSAndrzej Pietrasiewicz
32000a2430fSAndrzej Pietrasiewicz opts = kzalloc(sizeof(*opts), GFP_KERNEL);
32100a2430fSAndrzej Pietrasiewicz if (!opts)
32200a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
32300a2430fSAndrzej Pietrasiewicz
32400a2430fSAndrzej Pietrasiewicz opts->func_inst.free_func_inst = gser_free_inst;
32500a2430fSAndrzej Pietrasiewicz ret = gserial_alloc_line(&opts->port_num);
32600a2430fSAndrzej Pietrasiewicz if (ret) {
32700a2430fSAndrzej Pietrasiewicz kfree(opts);
32800a2430fSAndrzej Pietrasiewicz return ERR_PTR(ret);
32900a2430fSAndrzej Pietrasiewicz }
33000a2430fSAndrzej Pietrasiewicz config_group_init_type_name(&opts->func_inst.group, "",
33100a2430fSAndrzej Pietrasiewicz &serial_func_type);
33200a2430fSAndrzej Pietrasiewicz
33300a2430fSAndrzej Pietrasiewicz return &opts->func_inst;
33400a2430fSAndrzej Pietrasiewicz }
33500a2430fSAndrzej Pietrasiewicz
gser_free(struct usb_function * f)33600a2430fSAndrzej Pietrasiewicz static void gser_free(struct usb_function *f)
33700a2430fSAndrzej Pietrasiewicz {
33800a2430fSAndrzej Pietrasiewicz struct f_gser *serial;
33900a2430fSAndrzej Pietrasiewicz
34000a2430fSAndrzej Pietrasiewicz serial = func_to_gser(f);
34100a2430fSAndrzej Pietrasiewicz kfree(serial);
34200a2430fSAndrzej Pietrasiewicz }
34300a2430fSAndrzej Pietrasiewicz
gser_unbind(struct usb_configuration * c,struct usb_function * f)34400a2430fSAndrzej Pietrasiewicz static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
34500a2430fSAndrzej Pietrasiewicz {
346d6dd18efSWesley Cheng struct f_gser *gser = func_to_gser(f);
347d6dd18efSWesley Cheng
348d6dd18efSWesley Cheng /* Ensure port is disconnected before unbinding */
349d6dd18efSWesley Cheng gserial_disconnect(&gser->port);
35000a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f);
35100a2430fSAndrzej Pietrasiewicz }
35200a2430fSAndrzej Pietrasiewicz
gser_resume(struct usb_function * f)353e702a7c3SFabrice Gasnier static void gser_resume(struct usb_function *f)
354e702a7c3SFabrice Gasnier {
355e702a7c3SFabrice Gasnier struct f_gser *gser = func_to_gser(f);
356e702a7c3SFabrice Gasnier
357e702a7c3SFabrice Gasnier gserial_resume(&gser->port);
358e702a7c3SFabrice Gasnier }
359e702a7c3SFabrice Gasnier
gser_suspend(struct usb_function * f)360e702a7c3SFabrice Gasnier static void gser_suspend(struct usb_function *f)
361e702a7c3SFabrice Gasnier {
362e702a7c3SFabrice Gasnier struct f_gser *gser = func_to_gser(f);
363e702a7c3SFabrice Gasnier
364e702a7c3SFabrice Gasnier gserial_suspend(&gser->port);
365e702a7c3SFabrice Gasnier }
366e702a7c3SFabrice Gasnier
gser_alloc(struct usb_function_instance * fi)36700a2430fSAndrzej Pietrasiewicz static struct usb_function *gser_alloc(struct usb_function_instance *fi)
36800a2430fSAndrzej Pietrasiewicz {
36900a2430fSAndrzej Pietrasiewicz struct f_gser *gser;
37000a2430fSAndrzej Pietrasiewicz struct f_serial_opts *opts;
37100a2430fSAndrzej Pietrasiewicz
37200a2430fSAndrzej Pietrasiewicz /* allocate and initialize one new instance */
37300a2430fSAndrzej Pietrasiewicz gser = kzalloc(sizeof(*gser), GFP_KERNEL);
37400a2430fSAndrzej Pietrasiewicz if (!gser)
37500a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
37600a2430fSAndrzej Pietrasiewicz
37700a2430fSAndrzej Pietrasiewicz opts = container_of(fi, struct f_serial_opts, func_inst);
37800a2430fSAndrzej Pietrasiewicz
37900a2430fSAndrzej Pietrasiewicz gser->port_num = opts->port_num;
38000a2430fSAndrzej Pietrasiewicz
38100a2430fSAndrzej Pietrasiewicz gser->port.func.name = "gser";
38200a2430fSAndrzej Pietrasiewicz gser->port.func.strings = gser_strings;
38300a2430fSAndrzej Pietrasiewicz gser->port.func.bind = gser_bind;
38400a2430fSAndrzej Pietrasiewicz gser->port.func.unbind = gser_unbind;
38500a2430fSAndrzej Pietrasiewicz gser->port.func.set_alt = gser_set_alt;
38600a2430fSAndrzej Pietrasiewicz gser->port.func.disable = gser_disable;
38700a2430fSAndrzej Pietrasiewicz gser->port.func.free_func = gser_free;
388e702a7c3SFabrice Gasnier gser->port.func.resume = gser_resume;
389e702a7c3SFabrice Gasnier gser->port.func.suspend = gser_suspend;
39000a2430fSAndrzej Pietrasiewicz
39100a2430fSAndrzej Pietrasiewicz return &gser->port.func;
39200a2430fSAndrzej Pietrasiewicz }
39300a2430fSAndrzej Pietrasiewicz
39400a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
39500a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
39600a2430fSAndrzej Pietrasiewicz MODULE_AUTHOR("Al Borchers");
39700a2430fSAndrzej Pietrasiewicz MODULE_AUTHOR("David Brownell");
398