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