15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
200a2430fSAndrzej Pietrasiewicz /*
300a2430fSAndrzej Pietrasiewicz  * f_acm.c -- USB CDC serial (ACM) 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  * Copyright (C) 2009 by Samsung Electronics
900a2430fSAndrzej Pietrasiewicz  * Author: Michal Nazarewicz (mina86@mina86.com)
1000a2430fSAndrzej Pietrasiewicz  */
1100a2430fSAndrzej Pietrasiewicz 
1200a2430fSAndrzej Pietrasiewicz /* #define VERBOSE_DEBUG */
1300a2430fSAndrzej Pietrasiewicz 
1400a2430fSAndrzej Pietrasiewicz #include <linux/slab.h>
1500a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h>
1600a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
1700a2430fSAndrzej Pietrasiewicz #include <linux/device.h>
1800a2430fSAndrzej Pietrasiewicz #include <linux/err.h>
1900a2430fSAndrzej Pietrasiewicz 
2000a2430fSAndrzej Pietrasiewicz #include "u_serial.h"
2100a2430fSAndrzej Pietrasiewicz 
2200a2430fSAndrzej Pietrasiewicz 
2300a2430fSAndrzej Pietrasiewicz /*
2400a2430fSAndrzej Pietrasiewicz  * This CDC ACM function support just wraps control functions and
2500a2430fSAndrzej Pietrasiewicz  * notifications around the generic serial-over-usb code.
2600a2430fSAndrzej Pietrasiewicz  *
2700a2430fSAndrzej Pietrasiewicz  * Because CDC ACM is standardized by the USB-IF, many host operating
2800a2430fSAndrzej Pietrasiewicz  * systems have drivers for it.  Accordingly, ACM is the preferred
2900a2430fSAndrzej Pietrasiewicz  * interop solution for serial-port type connections.  The control
3000a2430fSAndrzej Pietrasiewicz  * models are often not necessary, and in any case don't do much in
3100a2430fSAndrzej Pietrasiewicz  * this bare-bones implementation.
3200a2430fSAndrzej Pietrasiewicz  *
3300a2430fSAndrzej Pietrasiewicz  * Note that even MS-Windows has some support for ACM.  However, that
3400a2430fSAndrzej Pietrasiewicz  * support is somewhat broken because when you use ACM in a composite
3500a2430fSAndrzej Pietrasiewicz  * device, having multiple interfaces confuses the poor OS.  It doesn't
3600a2430fSAndrzej Pietrasiewicz  * seem to understand CDC Union descriptors.  The new "association"
3700a2430fSAndrzej Pietrasiewicz  * descriptors (roughly equivalent to CDC Unions) may sometimes help.
3800a2430fSAndrzej Pietrasiewicz  */
3900a2430fSAndrzej Pietrasiewicz 
4000a2430fSAndrzej Pietrasiewicz struct f_acm {
4100a2430fSAndrzej Pietrasiewicz 	struct gserial			port;
4200a2430fSAndrzej Pietrasiewicz 	u8				ctrl_id, data_id;
4300a2430fSAndrzej Pietrasiewicz 	u8				port_num;
4400a2430fSAndrzej Pietrasiewicz 
4500a2430fSAndrzej Pietrasiewicz 	u8				pending;
4600a2430fSAndrzej Pietrasiewicz 
4700a2430fSAndrzej Pietrasiewicz 	/* lock is mostly for pending and notify_req ... they get accessed
4800a2430fSAndrzej Pietrasiewicz 	 * by callbacks both from tty (open/close/break) under its spinlock,
4900a2430fSAndrzej Pietrasiewicz 	 * and notify_req.complete() which can't use that lock.
5000a2430fSAndrzej Pietrasiewicz 	 */
5100a2430fSAndrzej Pietrasiewicz 	spinlock_t			lock;
5200a2430fSAndrzej Pietrasiewicz 
5300a2430fSAndrzej Pietrasiewicz 	struct usb_ep			*notify;
5400a2430fSAndrzej Pietrasiewicz 	struct usb_request		*notify_req;
5500a2430fSAndrzej Pietrasiewicz 
5600a2430fSAndrzej Pietrasiewicz 	struct usb_cdc_line_coding	port_line_coding;	/* 8-N-1 etc */
5700a2430fSAndrzej Pietrasiewicz 
5800a2430fSAndrzej Pietrasiewicz 	/* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
5900a2430fSAndrzej Pietrasiewicz 	u16				port_handshake_bits;
6000a2430fSAndrzej Pietrasiewicz 	/* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
6100a2430fSAndrzej Pietrasiewicz 	u16				serial_state;
6200a2430fSAndrzej Pietrasiewicz };
6300a2430fSAndrzej Pietrasiewicz 
func_to_acm(struct usb_function * f)6400a2430fSAndrzej Pietrasiewicz static inline struct f_acm *func_to_acm(struct usb_function *f)
6500a2430fSAndrzej Pietrasiewicz {
6600a2430fSAndrzej Pietrasiewicz 	return container_of(f, struct f_acm, port.func);
6700a2430fSAndrzej Pietrasiewicz }
6800a2430fSAndrzej Pietrasiewicz 
port_to_acm(struct gserial * p)6900a2430fSAndrzej Pietrasiewicz static inline struct f_acm *port_to_acm(struct gserial *p)
7000a2430fSAndrzej Pietrasiewicz {
7100a2430fSAndrzej Pietrasiewicz 	return container_of(p, struct f_acm, port);
7200a2430fSAndrzej Pietrasiewicz }
7300a2430fSAndrzej Pietrasiewicz 
7400a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
7500a2430fSAndrzej Pietrasiewicz 
7600a2430fSAndrzej Pietrasiewicz /* notification endpoint uses smallish and infrequent fixed-size messages */
7700a2430fSAndrzej Pietrasiewicz 
7800a2430fSAndrzej Pietrasiewicz #define GS_NOTIFY_INTERVAL_MS		32
7900a2430fSAndrzej Pietrasiewicz #define GS_NOTIFY_MAXPACKET		10	/* notification + 2 bytes */
8000a2430fSAndrzej Pietrasiewicz 
8100a2430fSAndrzej Pietrasiewicz /* interface and class descriptors: */
8200a2430fSAndrzej Pietrasiewicz 
8300a2430fSAndrzej Pietrasiewicz static struct usb_interface_assoc_descriptor
8400a2430fSAndrzej Pietrasiewicz acm_iad_descriptor = {
8500a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof acm_iad_descriptor,
8600a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
8700a2430fSAndrzej Pietrasiewicz 
8800a2430fSAndrzej Pietrasiewicz 	/* .bFirstInterface =	DYNAMIC, */
8900a2430fSAndrzej Pietrasiewicz 	.bInterfaceCount = 	2,	// control + data
9000a2430fSAndrzej Pietrasiewicz 	.bFunctionClass =	USB_CLASS_COMM,
9100a2430fSAndrzej Pietrasiewicz 	.bFunctionSubClass =	USB_CDC_SUBCLASS_ACM,
9200a2430fSAndrzej Pietrasiewicz 	.bFunctionProtocol =	USB_CDC_ACM_PROTO_AT_V25TER,
9300a2430fSAndrzej Pietrasiewicz 	/* .iFunction =		DYNAMIC */
9400a2430fSAndrzej Pietrasiewicz };
9500a2430fSAndrzej Pietrasiewicz 
9600a2430fSAndrzej Pietrasiewicz 
9700a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor acm_control_interface_desc = {
9800a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_INTERFACE_SIZE,
9900a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
10000a2430fSAndrzej Pietrasiewicz 	/* .bInterfaceNumber = DYNAMIC */
10100a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	1,
10200a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_COMM,
10300a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	USB_CDC_SUBCLASS_ACM,
10400a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	USB_CDC_ACM_PROTO_AT_V25TER,
10500a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
10600a2430fSAndrzej Pietrasiewicz };
10700a2430fSAndrzej Pietrasiewicz 
10800a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor acm_data_interface_desc = {
10900a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_INTERFACE_SIZE,
11000a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_INTERFACE,
11100a2430fSAndrzej Pietrasiewicz 	/* .bInterfaceNumber = DYNAMIC */
11200a2430fSAndrzej Pietrasiewicz 	.bNumEndpoints =	2,
11300a2430fSAndrzej Pietrasiewicz 	.bInterfaceClass =	USB_CLASS_CDC_DATA,
11400a2430fSAndrzej Pietrasiewicz 	.bInterfaceSubClass =	0,
11500a2430fSAndrzej Pietrasiewicz 	.bInterfaceProtocol =	0,
11600a2430fSAndrzej Pietrasiewicz 	/* .iInterface = DYNAMIC */
11700a2430fSAndrzej Pietrasiewicz };
11800a2430fSAndrzej Pietrasiewicz 
11900a2430fSAndrzej Pietrasiewicz static struct usb_cdc_header_desc acm_header_desc = {
12000a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(acm_header_desc),
12100a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
12200a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
12300a2430fSAndrzej Pietrasiewicz 	.bcdCDC =		cpu_to_le16(0x0110),
12400a2430fSAndrzej Pietrasiewicz };
12500a2430fSAndrzej Pietrasiewicz 
12600a2430fSAndrzej Pietrasiewicz static struct usb_cdc_call_mgmt_descriptor
12700a2430fSAndrzej Pietrasiewicz acm_call_mgmt_descriptor = {
12800a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(acm_call_mgmt_descriptor),
12900a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
13000a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
13100a2430fSAndrzej Pietrasiewicz 	.bmCapabilities =	0,
13200a2430fSAndrzej Pietrasiewicz 	/* .bDataInterface = DYNAMIC */
13300a2430fSAndrzej Pietrasiewicz };
13400a2430fSAndrzej Pietrasiewicz 
13500a2430fSAndrzej Pietrasiewicz static struct usb_cdc_acm_descriptor acm_descriptor = {
13600a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(acm_descriptor),
13700a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
13800a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
13900a2430fSAndrzej Pietrasiewicz 	.bmCapabilities =	USB_CDC_CAP_LINE,
14000a2430fSAndrzej Pietrasiewicz };
14100a2430fSAndrzej Pietrasiewicz 
14200a2430fSAndrzej Pietrasiewicz static struct usb_cdc_union_desc acm_union_desc = {
14300a2430fSAndrzej Pietrasiewicz 	.bLength =		sizeof(acm_union_desc),
14400a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_CS_INTERFACE,
14500a2430fSAndrzej Pietrasiewicz 	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
14600a2430fSAndrzej Pietrasiewicz 	/* .bMasterInterface0 =	DYNAMIC */
14700a2430fSAndrzej Pietrasiewicz 	/* .bSlaveInterface0 =	DYNAMIC */
14800a2430fSAndrzej Pietrasiewicz };
14900a2430fSAndrzej Pietrasiewicz 
15000a2430fSAndrzej Pietrasiewicz /* full speed support: */
15100a2430fSAndrzej Pietrasiewicz 
15200a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_fs_notify_desc = {
15300a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
15400a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
15500a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
15600a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
15700a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
15800a2430fSAndrzej Pietrasiewicz 	.bInterval =		GS_NOTIFY_INTERVAL_MS,
15900a2430fSAndrzej Pietrasiewicz };
16000a2430fSAndrzej Pietrasiewicz 
16100a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_fs_in_desc = {
16200a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
16300a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
16400a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
16500a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
16600a2430fSAndrzej Pietrasiewicz };
16700a2430fSAndrzej Pietrasiewicz 
16800a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_fs_out_desc = {
16900a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
17000a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
17100a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_OUT,
17200a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
17300a2430fSAndrzej Pietrasiewicz };
17400a2430fSAndrzej Pietrasiewicz 
17500a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *acm_fs_function[] = {
17600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_iad_descriptor,
17700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_control_interface_desc,
17800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_header_desc,
17900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
18000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_descriptor,
18100a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_union_desc,
18200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_fs_notify_desc,
18300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_data_interface_desc,
18400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_fs_in_desc,
18500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_fs_out_desc,
18600a2430fSAndrzej Pietrasiewicz 	NULL,
18700a2430fSAndrzej Pietrasiewicz };
18800a2430fSAndrzej Pietrasiewicz 
18900a2430fSAndrzej Pietrasiewicz /* high speed support: */
19000a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_hs_notify_desc = {
19100a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
19200a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
19300a2430fSAndrzej Pietrasiewicz 	.bEndpointAddress =	USB_DIR_IN,
19400a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_INT,
19500a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(GS_NOTIFY_MAXPACKET),
19600a2430fSAndrzej Pietrasiewicz 	.bInterval =		USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS),
19700a2430fSAndrzej Pietrasiewicz };
19800a2430fSAndrzej Pietrasiewicz 
19900a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_hs_in_desc = {
20000a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
20100a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
20200a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
20300a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
20400a2430fSAndrzej Pietrasiewicz };
20500a2430fSAndrzej Pietrasiewicz 
20600a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_hs_out_desc = {
20700a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
20800a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
20900a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
21000a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(512),
21100a2430fSAndrzej Pietrasiewicz };
21200a2430fSAndrzej Pietrasiewicz 
21300a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *acm_hs_function[] = {
21400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_iad_descriptor,
21500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_control_interface_desc,
21600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_header_desc,
21700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
21800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_descriptor,
21900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_union_desc,
22000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_hs_notify_desc,
22100a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_data_interface_desc,
22200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_hs_in_desc,
22300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_hs_out_desc,
22400a2430fSAndrzej Pietrasiewicz 	NULL,
22500a2430fSAndrzej Pietrasiewicz };
22600a2430fSAndrzej Pietrasiewicz 
22700a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_ss_in_desc = {
22800a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
22900a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
23000a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
23100a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
23200a2430fSAndrzej Pietrasiewicz };
23300a2430fSAndrzej Pietrasiewicz 
23400a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor acm_ss_out_desc = {
23500a2430fSAndrzej Pietrasiewicz 	.bLength =		USB_DT_ENDPOINT_SIZE,
23600a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =	USB_DT_ENDPOINT,
23700a2430fSAndrzej Pietrasiewicz 	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
23800a2430fSAndrzej Pietrasiewicz 	.wMaxPacketSize =	cpu_to_le16(1024),
23900a2430fSAndrzej Pietrasiewicz };
24000a2430fSAndrzej Pietrasiewicz 
24100a2430fSAndrzej Pietrasiewicz static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
24200a2430fSAndrzej Pietrasiewicz 	.bLength =              sizeof acm_ss_bulk_comp_desc,
24300a2430fSAndrzej Pietrasiewicz 	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
24400a2430fSAndrzej Pietrasiewicz };
24500a2430fSAndrzej Pietrasiewicz 
24600a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *acm_ss_function[] = {
24700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_iad_descriptor,
24800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_control_interface_desc,
24900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_header_desc,
25000a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
25100a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_descriptor,
25200a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_union_desc,
25300a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_hs_notify_desc,
25400a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
25500a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_data_interface_desc,
25600a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_ss_in_desc,
25700a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
25800a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_ss_out_desc,
25900a2430fSAndrzej Pietrasiewicz 	(struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
26000a2430fSAndrzej Pietrasiewicz 	NULL,
26100a2430fSAndrzej Pietrasiewicz };
26200a2430fSAndrzej Pietrasiewicz 
26300a2430fSAndrzej Pietrasiewicz /* string descriptors: */
26400a2430fSAndrzej Pietrasiewicz 
26500a2430fSAndrzej Pietrasiewicz #define ACM_CTRL_IDX	0
26600a2430fSAndrzej Pietrasiewicz #define ACM_DATA_IDX	1
26700a2430fSAndrzej Pietrasiewicz #define ACM_IAD_IDX	2
26800a2430fSAndrzej Pietrasiewicz 
26900a2430fSAndrzej Pietrasiewicz /* static strings, in UTF-8 */
27000a2430fSAndrzej Pietrasiewicz static struct usb_string acm_string_defs[] = {
27100a2430fSAndrzej Pietrasiewicz 	[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
27200a2430fSAndrzej Pietrasiewicz 	[ACM_DATA_IDX].s = "CDC ACM Data",
27300a2430fSAndrzej Pietrasiewicz 	[ACM_IAD_IDX ].s = "CDC Serial",
27400a2430fSAndrzej Pietrasiewicz 	{  } /* end of list */
27500a2430fSAndrzej Pietrasiewicz };
27600a2430fSAndrzej Pietrasiewicz 
27700a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings acm_string_table = {
27800a2430fSAndrzej Pietrasiewicz 	.language =		0x0409,	/* en-us */
27900a2430fSAndrzej Pietrasiewicz 	.strings =		acm_string_defs,
28000a2430fSAndrzej Pietrasiewicz };
28100a2430fSAndrzej Pietrasiewicz 
28200a2430fSAndrzej Pietrasiewicz static struct usb_gadget_strings *acm_strings[] = {
28300a2430fSAndrzej Pietrasiewicz 	&acm_string_table,
28400a2430fSAndrzej Pietrasiewicz 	NULL,
28500a2430fSAndrzej Pietrasiewicz };
28600a2430fSAndrzej Pietrasiewicz 
28700a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
28800a2430fSAndrzej Pietrasiewicz 
28900a2430fSAndrzej Pietrasiewicz /* ACM control ... data handling is delegated to tty library code.
29000a2430fSAndrzej Pietrasiewicz  * The main task of this function is to activate and deactivate
29100a2430fSAndrzej Pietrasiewicz  * that code based on device state; track parameters like line
29200a2430fSAndrzej Pietrasiewicz  * speed, handshake state, and so on; and issue notifications.
29300a2430fSAndrzej Pietrasiewicz  */
29400a2430fSAndrzej Pietrasiewicz 
acm_complete_set_line_coding(struct usb_ep * ep,struct usb_request * req)29500a2430fSAndrzej Pietrasiewicz static void acm_complete_set_line_coding(struct usb_ep *ep,
29600a2430fSAndrzej Pietrasiewicz 		struct usb_request *req)
29700a2430fSAndrzej Pietrasiewicz {
29800a2430fSAndrzej Pietrasiewicz 	struct f_acm	*acm = ep->driver_data;
29900a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = acm->port.func.config->cdev;
30000a2430fSAndrzej Pietrasiewicz 
30100a2430fSAndrzej Pietrasiewicz 	if (req->status != 0) {
302b8b0ea51SRichard Leitner 		dev_dbg(&cdev->gadget->dev, "acm ttyGS%d completion, err %d\n",
30300a2430fSAndrzej Pietrasiewicz 			acm->port_num, req->status);
30400a2430fSAndrzej Pietrasiewicz 		return;
30500a2430fSAndrzej Pietrasiewicz 	}
30600a2430fSAndrzej Pietrasiewicz 
30700a2430fSAndrzej Pietrasiewicz 	/* normal completion */
30800a2430fSAndrzej Pietrasiewicz 	if (req->actual != sizeof(acm->port_line_coding)) {
309b8b0ea51SRichard Leitner 		dev_dbg(&cdev->gadget->dev, "acm ttyGS%d short resp, len %d\n",
31000a2430fSAndrzej Pietrasiewicz 			acm->port_num, req->actual);
31100a2430fSAndrzej Pietrasiewicz 		usb_ep_set_halt(ep);
31200a2430fSAndrzej Pietrasiewicz 	} else {
31300a2430fSAndrzej Pietrasiewicz 		struct usb_cdc_line_coding	*value = req->buf;
31400a2430fSAndrzej Pietrasiewicz 
31500a2430fSAndrzej Pietrasiewicz 		/* REVISIT:  we currently just remember this data.
31600a2430fSAndrzej Pietrasiewicz 		 * If we change that, (a) validate it first, then
31700a2430fSAndrzej Pietrasiewicz 		 * (b) update whatever hardware needs updating,
31800a2430fSAndrzej Pietrasiewicz 		 * (c) worry about locking.  This is information on
31900a2430fSAndrzej Pietrasiewicz 		 * the order of 9600-8-N-1 ... most of which means
32000a2430fSAndrzej Pietrasiewicz 		 * nothing unless we control a real RS232 line.
32100a2430fSAndrzej Pietrasiewicz 		 */
32200a2430fSAndrzej Pietrasiewicz 		acm->port_line_coding = *value;
32300a2430fSAndrzej Pietrasiewicz 	}
32400a2430fSAndrzej Pietrasiewicz }
32500a2430fSAndrzej Pietrasiewicz 
3267e824f28SMichael Grzeschik static int acm_send_break(struct gserial *port, int duration);
3277e824f28SMichael Grzeschik 
acm_setup(struct usb_function * f,const struct usb_ctrlrequest * ctrl)32800a2430fSAndrzej Pietrasiewicz static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
32900a2430fSAndrzej Pietrasiewicz {
33000a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = func_to_acm(f);
33100a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
33200a2430fSAndrzej Pietrasiewicz 	struct usb_request	*req = cdev->req;
33300a2430fSAndrzej Pietrasiewicz 	int			value = -EOPNOTSUPP;
33400a2430fSAndrzej Pietrasiewicz 	u16			w_index = le16_to_cpu(ctrl->wIndex);
33500a2430fSAndrzej Pietrasiewicz 	u16			w_value = le16_to_cpu(ctrl->wValue);
33600a2430fSAndrzej Pietrasiewicz 	u16			w_length = le16_to_cpu(ctrl->wLength);
33700a2430fSAndrzej Pietrasiewicz 
33800a2430fSAndrzej Pietrasiewicz 	/* composite driver infrastructure handles everything except
33900a2430fSAndrzej Pietrasiewicz 	 * CDC class messages; interface activation uses set_alt().
34000a2430fSAndrzej Pietrasiewicz 	 *
34100a2430fSAndrzej Pietrasiewicz 	 * Note CDC spec table 4 lists the ACM request profile.  It requires
34200a2430fSAndrzej Pietrasiewicz 	 * encapsulated command support ... we don't handle any, and respond
34300a2430fSAndrzej Pietrasiewicz 	 * to them by stalling.  Options include get/set/clear comm features
34400a2430fSAndrzej Pietrasiewicz 	 * (not that useful) and SEND_BREAK.
34500a2430fSAndrzej Pietrasiewicz 	 */
34600a2430fSAndrzej Pietrasiewicz 	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
34700a2430fSAndrzej Pietrasiewicz 
34800a2430fSAndrzej Pietrasiewicz 	/* SET_LINE_CODING ... just read and save what the host sends */
34900a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
35000a2430fSAndrzej Pietrasiewicz 			| USB_CDC_REQ_SET_LINE_CODING:
35100a2430fSAndrzej Pietrasiewicz 		if (w_length != sizeof(struct usb_cdc_line_coding)
35200a2430fSAndrzej Pietrasiewicz 				|| w_index != acm->ctrl_id)
35300a2430fSAndrzej Pietrasiewicz 			goto invalid;
35400a2430fSAndrzej Pietrasiewicz 
35500a2430fSAndrzej Pietrasiewicz 		value = w_length;
35600a2430fSAndrzej Pietrasiewicz 		cdev->gadget->ep0->driver_data = acm;
35700a2430fSAndrzej Pietrasiewicz 		req->complete = acm_complete_set_line_coding;
35800a2430fSAndrzej Pietrasiewicz 		break;
35900a2430fSAndrzej Pietrasiewicz 
36000a2430fSAndrzej Pietrasiewicz 	/* GET_LINE_CODING ... return what host sent, or initial value */
36100a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
36200a2430fSAndrzej Pietrasiewicz 			| USB_CDC_REQ_GET_LINE_CODING:
36300a2430fSAndrzej Pietrasiewicz 		if (w_index != acm->ctrl_id)
36400a2430fSAndrzej Pietrasiewicz 			goto invalid;
36500a2430fSAndrzej Pietrasiewicz 
36600a2430fSAndrzej Pietrasiewicz 		value = min_t(unsigned, w_length,
36700a2430fSAndrzej Pietrasiewicz 				sizeof(struct usb_cdc_line_coding));
36800a2430fSAndrzej Pietrasiewicz 		memcpy(req->buf, &acm->port_line_coding, value);
36900a2430fSAndrzej Pietrasiewicz 		break;
37000a2430fSAndrzej Pietrasiewicz 
37100a2430fSAndrzej Pietrasiewicz 	/* SET_CONTROL_LINE_STATE ... save what the host sent */
37200a2430fSAndrzej Pietrasiewicz 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
37300a2430fSAndrzej Pietrasiewicz 			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
37400a2430fSAndrzej Pietrasiewicz 		if (w_index != acm->ctrl_id)
37500a2430fSAndrzej Pietrasiewicz 			goto invalid;
37600a2430fSAndrzej Pietrasiewicz 
37700a2430fSAndrzej Pietrasiewicz 		value = 0;
37800a2430fSAndrzej Pietrasiewicz 
37900a2430fSAndrzej Pietrasiewicz 		/* FIXME we should not allow data to flow until the
380f4beed1eSJohan Hovold 		 * host sets the USB_CDC_CTRL_DTR bit; and when it clears
38100a2430fSAndrzej Pietrasiewicz 		 * that bit, we should return to that no-flow state.
38200a2430fSAndrzej Pietrasiewicz 		 */
38300a2430fSAndrzej Pietrasiewicz 		acm->port_handshake_bits = w_value;
38400a2430fSAndrzej Pietrasiewicz 		break;
38500a2430fSAndrzej Pietrasiewicz 
3867e824f28SMichael Grzeschik 	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
3877e824f28SMichael Grzeschik 			| USB_CDC_REQ_SEND_BREAK:
3887e824f28SMichael Grzeschik 		if (w_index != acm->ctrl_id)
3897e824f28SMichael Grzeschik 			goto invalid;
3907e824f28SMichael Grzeschik 
3917e824f28SMichael Grzeschik 		acm_send_break(&acm->port, w_value);
3927e824f28SMichael Grzeschik 		break;
3937e824f28SMichael Grzeschik 
39400a2430fSAndrzej Pietrasiewicz 	default:
39500a2430fSAndrzej Pietrasiewicz invalid:
396b8b0ea51SRichard Leitner 		dev_vdbg(&cdev->gadget->dev,
397b8b0ea51SRichard Leitner 			 "invalid control req%02x.%02x v%04x i%04x l%d\n",
39800a2430fSAndrzej Pietrasiewicz 			 ctrl->bRequestType, ctrl->bRequest,
39900a2430fSAndrzej Pietrasiewicz 			 w_value, w_index, w_length);
40000a2430fSAndrzej Pietrasiewicz 	}
40100a2430fSAndrzej Pietrasiewicz 
40200a2430fSAndrzej Pietrasiewicz 	/* respond with data transfer or status phase? */
40300a2430fSAndrzej Pietrasiewicz 	if (value >= 0) {
404b8b0ea51SRichard Leitner 		dev_dbg(&cdev->gadget->dev,
405b8b0ea51SRichard Leitner 			"acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
40600a2430fSAndrzej Pietrasiewicz 			acm->port_num, ctrl->bRequestType, ctrl->bRequest,
40700a2430fSAndrzej Pietrasiewicz 			w_value, w_index, w_length);
40800a2430fSAndrzej Pietrasiewicz 		req->zero = 0;
40900a2430fSAndrzej Pietrasiewicz 		req->length = value;
41000a2430fSAndrzej Pietrasiewicz 		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
41100a2430fSAndrzej Pietrasiewicz 		if (value < 0)
41200a2430fSAndrzej Pietrasiewicz 			ERROR(cdev, "acm response on ttyGS%d, err %d\n",
41300a2430fSAndrzej Pietrasiewicz 					acm->port_num, value);
41400a2430fSAndrzej Pietrasiewicz 	}
41500a2430fSAndrzej Pietrasiewicz 
41600a2430fSAndrzej Pietrasiewicz 	/* device either stalls (value < 0) or reports success */
41700a2430fSAndrzej Pietrasiewicz 	return value;
41800a2430fSAndrzej Pietrasiewicz }
41900a2430fSAndrzej Pietrasiewicz 
acm_set_alt(struct usb_function * f,unsigned intf,unsigned alt)42000a2430fSAndrzej Pietrasiewicz static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
42100a2430fSAndrzej Pietrasiewicz {
42200a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = func_to_acm(f);
42300a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
42400a2430fSAndrzej Pietrasiewicz 
42500a2430fSAndrzej Pietrasiewicz 	/* we know alt == 0, so this is an activation or a reset */
42600a2430fSAndrzej Pietrasiewicz 
42700a2430fSAndrzej Pietrasiewicz 	if (intf == acm->ctrl_id) {
428e7a0ed3fSMichał Mirosław 		if (acm->notify->enabled) {
429b8b0ea51SRichard Leitner 			dev_vdbg(&cdev->gadget->dev,
430b8b0ea51SRichard Leitner 					"reset acm control interface %d\n", intf);
43100a2430fSAndrzej Pietrasiewicz 			usb_ep_disable(acm->notify);
432e7a0ed3fSMichał Mirosław 		}
43352ec49a5SFelipe Balbi 
43452ec49a5SFelipe Balbi 		if (!acm->notify->desc)
43500a2430fSAndrzej Pietrasiewicz 			if (config_ep_by_speed(cdev->gadget, f, acm->notify))
43600a2430fSAndrzej Pietrasiewicz 				return -EINVAL;
43752ec49a5SFelipe Balbi 
43800a2430fSAndrzej Pietrasiewicz 		usb_ep_enable(acm->notify);
43900a2430fSAndrzej Pietrasiewicz 
44000a2430fSAndrzej Pietrasiewicz 	} else if (intf == acm->data_id) {
4414aab757cSRobert Baldyga 		if (acm->notify->enabled) {
442b8b0ea51SRichard Leitner 			dev_dbg(&cdev->gadget->dev,
443b8b0ea51SRichard Leitner 				"reset acm ttyGS%d\n", acm->port_num);
44400a2430fSAndrzej Pietrasiewicz 			gserial_disconnect(&acm->port);
44500a2430fSAndrzej Pietrasiewicz 		}
44600a2430fSAndrzej Pietrasiewicz 		if (!acm->port.in->desc || !acm->port.out->desc) {
447b8b0ea51SRichard Leitner 			dev_dbg(&cdev->gadget->dev,
448b8b0ea51SRichard Leitner 				"activate acm ttyGS%d\n", acm->port_num);
44900a2430fSAndrzej Pietrasiewicz 			if (config_ep_by_speed(cdev->gadget, f,
45000a2430fSAndrzej Pietrasiewicz 					       acm->port.in) ||
45100a2430fSAndrzej Pietrasiewicz 			    config_ep_by_speed(cdev->gadget, f,
45200a2430fSAndrzej Pietrasiewicz 					       acm->port.out)) {
45300a2430fSAndrzej Pietrasiewicz 				acm->port.in->desc = NULL;
45400a2430fSAndrzej Pietrasiewicz 				acm->port.out->desc = NULL;
45500a2430fSAndrzej Pietrasiewicz 				return -EINVAL;
45600a2430fSAndrzej Pietrasiewicz 			}
45700a2430fSAndrzej Pietrasiewicz 		}
45800a2430fSAndrzej Pietrasiewicz 		gserial_connect(&acm->port, acm->port_num);
45900a2430fSAndrzej Pietrasiewicz 
46000a2430fSAndrzej Pietrasiewicz 	} else
46100a2430fSAndrzej Pietrasiewicz 		return -EINVAL;
46200a2430fSAndrzej Pietrasiewicz 
46300a2430fSAndrzej Pietrasiewicz 	return 0;
46400a2430fSAndrzej Pietrasiewicz }
46500a2430fSAndrzej Pietrasiewicz 
acm_disable(struct usb_function * f)46600a2430fSAndrzej Pietrasiewicz static void acm_disable(struct usb_function *f)
46700a2430fSAndrzej Pietrasiewicz {
46800a2430fSAndrzej Pietrasiewicz 	struct f_acm	*acm = func_to_acm(f);
46900a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = f->config->cdev;
47000a2430fSAndrzej Pietrasiewicz 
471b8b0ea51SRichard Leitner 	dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num);
47200a2430fSAndrzej Pietrasiewicz 	gserial_disconnect(&acm->port);
47300a2430fSAndrzej Pietrasiewicz 	usb_ep_disable(acm->notify);
47400a2430fSAndrzej Pietrasiewicz }
47500a2430fSAndrzej Pietrasiewicz 
47600a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
47700a2430fSAndrzej Pietrasiewicz 
47800a2430fSAndrzej Pietrasiewicz /**
47900a2430fSAndrzej Pietrasiewicz  * acm_cdc_notify - issue CDC notification to host
48000a2430fSAndrzej Pietrasiewicz  * @acm: wraps host to be notified
48100a2430fSAndrzej Pietrasiewicz  * @type: notification type
48200a2430fSAndrzej Pietrasiewicz  * @value: Refer to cdc specs, wValue field.
48300a2430fSAndrzej Pietrasiewicz  * @data: data to be sent
48400a2430fSAndrzej Pietrasiewicz  * @length: size of data
48500a2430fSAndrzej Pietrasiewicz  * Context: irqs blocked, acm->lock held, acm_notify_req non-null
48600a2430fSAndrzej Pietrasiewicz  *
48700a2430fSAndrzej Pietrasiewicz  * Returns zero on success or a negative errno.
48800a2430fSAndrzej Pietrasiewicz  *
48900a2430fSAndrzej Pietrasiewicz  * See section 6.3.5 of the CDC 1.1 specification for information
49000a2430fSAndrzej Pietrasiewicz  * about the only notification we issue:  SerialState change.
49100a2430fSAndrzej Pietrasiewicz  */
acm_cdc_notify(struct f_acm * acm,u8 type,u16 value,void * data,unsigned length)49200a2430fSAndrzej Pietrasiewicz static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
49300a2430fSAndrzej Pietrasiewicz 		void *data, unsigned length)
49400a2430fSAndrzej Pietrasiewicz {
49500a2430fSAndrzej Pietrasiewicz 	struct usb_ep			*ep = acm->notify;
49600a2430fSAndrzej Pietrasiewicz 	struct usb_request		*req;
49700a2430fSAndrzej Pietrasiewicz 	struct usb_cdc_notification	*notify;
49800a2430fSAndrzej Pietrasiewicz 	const unsigned			len = sizeof(*notify) + length;
49900a2430fSAndrzej Pietrasiewicz 	void				*buf;
50000a2430fSAndrzej Pietrasiewicz 	int				status;
50100a2430fSAndrzej Pietrasiewicz 
50200a2430fSAndrzej Pietrasiewicz 	req = acm->notify_req;
50300a2430fSAndrzej Pietrasiewicz 	acm->notify_req = NULL;
50400a2430fSAndrzej Pietrasiewicz 	acm->pending = false;
50500a2430fSAndrzej Pietrasiewicz 
50600a2430fSAndrzej Pietrasiewicz 	req->length = len;
50700a2430fSAndrzej Pietrasiewicz 	notify = req->buf;
50800a2430fSAndrzej Pietrasiewicz 	buf = notify + 1;
50900a2430fSAndrzej Pietrasiewicz 
51000a2430fSAndrzej Pietrasiewicz 	notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
51100a2430fSAndrzej Pietrasiewicz 			| USB_RECIP_INTERFACE;
51200a2430fSAndrzej Pietrasiewicz 	notify->bNotificationType = type;
51300a2430fSAndrzej Pietrasiewicz 	notify->wValue = cpu_to_le16(value);
51400a2430fSAndrzej Pietrasiewicz 	notify->wIndex = cpu_to_le16(acm->ctrl_id);
51500a2430fSAndrzej Pietrasiewicz 	notify->wLength = cpu_to_le16(length);
51600a2430fSAndrzej Pietrasiewicz 	memcpy(buf, data, length);
51700a2430fSAndrzej Pietrasiewicz 
51800a2430fSAndrzej Pietrasiewicz 	/* ep_queue() can complete immediately if it fills the fifo... */
51900a2430fSAndrzej Pietrasiewicz 	spin_unlock(&acm->lock);
52000a2430fSAndrzej Pietrasiewicz 	status = usb_ep_queue(ep, req, GFP_ATOMIC);
52100a2430fSAndrzej Pietrasiewicz 	spin_lock(&acm->lock);
52200a2430fSAndrzej Pietrasiewicz 
52300a2430fSAndrzej Pietrasiewicz 	if (status < 0) {
52400a2430fSAndrzej Pietrasiewicz 		ERROR(acm->port.func.config->cdev,
52500a2430fSAndrzej Pietrasiewicz 				"acm ttyGS%d can't notify serial state, %d\n",
52600a2430fSAndrzej Pietrasiewicz 				acm->port_num, status);
52700a2430fSAndrzej Pietrasiewicz 		acm->notify_req = req;
52800a2430fSAndrzej Pietrasiewicz 	}
52900a2430fSAndrzej Pietrasiewicz 
53000a2430fSAndrzej Pietrasiewicz 	return status;
53100a2430fSAndrzej Pietrasiewicz }
53200a2430fSAndrzej Pietrasiewicz 
acm_notify_serial_state(struct f_acm * acm)53300a2430fSAndrzej Pietrasiewicz static int acm_notify_serial_state(struct f_acm *acm)
53400a2430fSAndrzej Pietrasiewicz {
53500a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = acm->port.func.config->cdev;
53600a2430fSAndrzej Pietrasiewicz 	int			status;
537cdd7928dSOliver Neukum 	__le16			serial_state;
53800a2430fSAndrzej Pietrasiewicz 
53900a2430fSAndrzej Pietrasiewicz 	spin_lock(&acm->lock);
54000a2430fSAndrzej Pietrasiewicz 	if (acm->notify_req) {
541b8b0ea51SRichard Leitner 		dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n",
54200a2430fSAndrzej Pietrasiewicz 			acm->port_num, acm->serial_state);
543cdd7928dSOliver Neukum 		serial_state = cpu_to_le16(acm->serial_state);
54400a2430fSAndrzej Pietrasiewicz 		status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
545cdd7928dSOliver Neukum 				0, &serial_state, sizeof(acm->serial_state));
54600a2430fSAndrzej Pietrasiewicz 	} else {
54700a2430fSAndrzej Pietrasiewicz 		acm->pending = true;
54800a2430fSAndrzej Pietrasiewicz 		status = 0;
54900a2430fSAndrzej Pietrasiewicz 	}
55000a2430fSAndrzej Pietrasiewicz 	spin_unlock(&acm->lock);
55100a2430fSAndrzej Pietrasiewicz 	return status;
55200a2430fSAndrzej Pietrasiewicz }
55300a2430fSAndrzej Pietrasiewicz 
acm_cdc_notify_complete(struct usb_ep * ep,struct usb_request * req)55400a2430fSAndrzej Pietrasiewicz static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
55500a2430fSAndrzej Pietrasiewicz {
55600a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = req->context;
55700a2430fSAndrzej Pietrasiewicz 	u8			doit = false;
55800a2430fSAndrzej Pietrasiewicz 
55900a2430fSAndrzej Pietrasiewicz 	/* on this call path we do NOT hold the port spinlock,
56000a2430fSAndrzej Pietrasiewicz 	 * which is why ACM needs its own spinlock
56100a2430fSAndrzej Pietrasiewicz 	 */
56200a2430fSAndrzej Pietrasiewicz 	spin_lock(&acm->lock);
56300a2430fSAndrzej Pietrasiewicz 	if (req->status != -ESHUTDOWN)
56400a2430fSAndrzej Pietrasiewicz 		doit = acm->pending;
56500a2430fSAndrzej Pietrasiewicz 	acm->notify_req = req;
56600a2430fSAndrzej Pietrasiewicz 	spin_unlock(&acm->lock);
56700a2430fSAndrzej Pietrasiewicz 
56800a2430fSAndrzej Pietrasiewicz 	if (doit)
56900a2430fSAndrzej Pietrasiewicz 		acm_notify_serial_state(acm);
57000a2430fSAndrzej Pietrasiewicz }
57100a2430fSAndrzej Pietrasiewicz 
57200a2430fSAndrzej Pietrasiewicz /* connect == the TTY link is open */
57300a2430fSAndrzej Pietrasiewicz 
acm_connect(struct gserial * port)57400a2430fSAndrzej Pietrasiewicz static void acm_connect(struct gserial *port)
57500a2430fSAndrzej Pietrasiewicz {
57600a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = port_to_acm(port);
57700a2430fSAndrzej Pietrasiewicz 
578f4beed1eSJohan Hovold 	acm->serial_state |= USB_CDC_SERIAL_STATE_DSR | USB_CDC_SERIAL_STATE_DCD;
57900a2430fSAndrzej Pietrasiewicz 	acm_notify_serial_state(acm);
58000a2430fSAndrzej Pietrasiewicz }
58100a2430fSAndrzej Pietrasiewicz 
acm_disconnect(struct gserial * port)58200a2430fSAndrzej Pietrasiewicz static void acm_disconnect(struct gserial *port)
58300a2430fSAndrzej Pietrasiewicz {
58400a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = port_to_acm(port);
58500a2430fSAndrzej Pietrasiewicz 
586f4beed1eSJohan Hovold 	acm->serial_state &= ~(USB_CDC_SERIAL_STATE_DSR | USB_CDC_SERIAL_STATE_DCD);
58700a2430fSAndrzej Pietrasiewicz 	acm_notify_serial_state(acm);
58800a2430fSAndrzej Pietrasiewicz }
58900a2430fSAndrzej Pietrasiewicz 
acm_send_break(struct gserial * port,int duration)59000a2430fSAndrzej Pietrasiewicz static int acm_send_break(struct gserial *port, int duration)
59100a2430fSAndrzej Pietrasiewicz {
59200a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = port_to_acm(port);
59300a2430fSAndrzej Pietrasiewicz 	u16			state;
59400a2430fSAndrzej Pietrasiewicz 
59500a2430fSAndrzej Pietrasiewicz 	state = acm->serial_state;
596f4beed1eSJohan Hovold 	state &= ~USB_CDC_SERIAL_STATE_BREAK;
59700a2430fSAndrzej Pietrasiewicz 	if (duration)
598f4beed1eSJohan Hovold 		state |= USB_CDC_SERIAL_STATE_BREAK;
59900a2430fSAndrzej Pietrasiewicz 
60000a2430fSAndrzej Pietrasiewicz 	acm->serial_state = state;
60100a2430fSAndrzej Pietrasiewicz 	return acm_notify_serial_state(acm);
60200a2430fSAndrzej Pietrasiewicz }
60300a2430fSAndrzej Pietrasiewicz 
60400a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
60500a2430fSAndrzej Pietrasiewicz 
60600a2430fSAndrzej Pietrasiewicz /* ACM function driver setup/binding */
60700a2430fSAndrzej Pietrasiewicz static int
acm_bind(struct usb_configuration * c,struct usb_function * f)60800a2430fSAndrzej Pietrasiewicz acm_bind(struct usb_configuration *c, struct usb_function *f)
60900a2430fSAndrzej Pietrasiewicz {
61000a2430fSAndrzej Pietrasiewicz 	struct usb_composite_dev *cdev = c->cdev;
61100a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = func_to_acm(f);
61200a2430fSAndrzej Pietrasiewicz 	struct usb_string	*us;
61300a2430fSAndrzej Pietrasiewicz 	int			status;
61400a2430fSAndrzej Pietrasiewicz 	struct usb_ep		*ep;
61500a2430fSAndrzej Pietrasiewicz 
61600a2430fSAndrzej Pietrasiewicz 	/* REVISIT might want instance-specific strings to help
61700a2430fSAndrzej Pietrasiewicz 	 * distinguish instances ...
61800a2430fSAndrzej Pietrasiewicz 	 */
61900a2430fSAndrzej Pietrasiewicz 
62000a2430fSAndrzej Pietrasiewicz 	/* maybe allocate device-global string IDs, and patch descriptors */
62100a2430fSAndrzej Pietrasiewicz 	us = usb_gstrings_attach(cdev, acm_strings,
62200a2430fSAndrzej Pietrasiewicz 			ARRAY_SIZE(acm_string_defs));
62300a2430fSAndrzej Pietrasiewicz 	if (IS_ERR(us))
62400a2430fSAndrzej Pietrasiewicz 		return PTR_ERR(us);
62500a2430fSAndrzej Pietrasiewicz 	acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id;
62600a2430fSAndrzej Pietrasiewicz 	acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id;
62700a2430fSAndrzej Pietrasiewicz 	acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id;
62800a2430fSAndrzej Pietrasiewicz 
62900a2430fSAndrzej Pietrasiewicz 	/* allocate instance-specific interface IDs, and patch descriptors */
63000a2430fSAndrzej Pietrasiewicz 	status = usb_interface_id(c, f);
63100a2430fSAndrzej Pietrasiewicz 	if (status < 0)
63200a2430fSAndrzej Pietrasiewicz 		goto fail;
63300a2430fSAndrzej Pietrasiewicz 	acm->ctrl_id = status;
63400a2430fSAndrzej Pietrasiewicz 	acm_iad_descriptor.bFirstInterface = status;
63500a2430fSAndrzej Pietrasiewicz 
63600a2430fSAndrzej Pietrasiewicz 	acm_control_interface_desc.bInterfaceNumber = status;
63700a2430fSAndrzej Pietrasiewicz 	acm_union_desc .bMasterInterface0 = status;
63800a2430fSAndrzej Pietrasiewicz 
63900a2430fSAndrzej Pietrasiewicz 	status = usb_interface_id(c, f);
64000a2430fSAndrzej Pietrasiewicz 	if (status < 0)
64100a2430fSAndrzej Pietrasiewicz 		goto fail;
64200a2430fSAndrzej Pietrasiewicz 	acm->data_id = status;
64300a2430fSAndrzej Pietrasiewicz 
64400a2430fSAndrzej Pietrasiewicz 	acm_data_interface_desc.bInterfaceNumber = status;
64500a2430fSAndrzej Pietrasiewicz 	acm_union_desc.bSlaveInterface0 = status;
64600a2430fSAndrzej Pietrasiewicz 	acm_call_mgmt_descriptor.bDataInterface = status;
64700a2430fSAndrzej Pietrasiewicz 
64800a2430fSAndrzej Pietrasiewicz 	status = -ENODEV;
64900a2430fSAndrzej Pietrasiewicz 
65000a2430fSAndrzej Pietrasiewicz 	/* allocate instance-specific endpoints */
65100a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
65200a2430fSAndrzej Pietrasiewicz 	if (!ep)
65300a2430fSAndrzej Pietrasiewicz 		goto fail;
65400a2430fSAndrzej Pietrasiewicz 	acm->port.in = ep;
65500a2430fSAndrzej Pietrasiewicz 
65600a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
65700a2430fSAndrzej Pietrasiewicz 	if (!ep)
65800a2430fSAndrzej Pietrasiewicz 		goto fail;
65900a2430fSAndrzej Pietrasiewicz 	acm->port.out = ep;
66000a2430fSAndrzej Pietrasiewicz 
66100a2430fSAndrzej Pietrasiewicz 	ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
66200a2430fSAndrzej Pietrasiewicz 	if (!ep)
66300a2430fSAndrzej Pietrasiewicz 		goto fail;
66400a2430fSAndrzej Pietrasiewicz 	acm->notify = ep;
66500a2430fSAndrzej Pietrasiewicz 
66600a2430fSAndrzej Pietrasiewicz 	/* allocate notification */
66700a2430fSAndrzej Pietrasiewicz 	acm->notify_req = gs_alloc_req(ep,
66800a2430fSAndrzej Pietrasiewicz 			sizeof(struct usb_cdc_notification) + 2,
66900a2430fSAndrzej Pietrasiewicz 			GFP_KERNEL);
67000a2430fSAndrzej Pietrasiewicz 	if (!acm->notify_req)
67100a2430fSAndrzej Pietrasiewicz 		goto fail;
67200a2430fSAndrzej Pietrasiewicz 
67300a2430fSAndrzej Pietrasiewicz 	acm->notify_req->complete = acm_cdc_notify_complete;
67400a2430fSAndrzej Pietrasiewicz 	acm->notify_req->context = acm;
67500a2430fSAndrzej Pietrasiewicz 
67600a2430fSAndrzej Pietrasiewicz 	/* support all relevant hardware speeds... we expect that when
67700a2430fSAndrzej Pietrasiewicz 	 * hardware is dual speed, all bulk-capable endpoints work at
67800a2430fSAndrzej Pietrasiewicz 	 * both speeds
67900a2430fSAndrzej Pietrasiewicz 	 */
68000a2430fSAndrzej Pietrasiewicz 	acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
68100a2430fSAndrzej Pietrasiewicz 	acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
68200a2430fSAndrzej Pietrasiewicz 	acm_hs_notify_desc.bEndpointAddress =
68300a2430fSAndrzej Pietrasiewicz 		acm_fs_notify_desc.bEndpointAddress;
68400a2430fSAndrzej Pietrasiewicz 
68500a2430fSAndrzej Pietrasiewicz 	acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress;
68600a2430fSAndrzej Pietrasiewicz 	acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress;
68700a2430fSAndrzej Pietrasiewicz 
68800a2430fSAndrzej Pietrasiewicz 	status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function,
6893ee05c20Staehyun.cho 			acm_ss_function, acm_ss_function);
69000a2430fSAndrzej Pietrasiewicz 	if (status)
69100a2430fSAndrzej Pietrasiewicz 		goto fail;
69200a2430fSAndrzej Pietrasiewicz 
693b8b0ea51SRichard Leitner 	dev_dbg(&cdev->gadget->dev,
694*333ab99eSLinyu Yuan 		"acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n",
69500a2430fSAndrzej Pietrasiewicz 		acm->port_num,
69600a2430fSAndrzej Pietrasiewicz 		acm->port.in->name, acm->port.out->name,
69700a2430fSAndrzej Pietrasiewicz 		acm->notify->name);
69800a2430fSAndrzej Pietrasiewicz 	return 0;
69900a2430fSAndrzej Pietrasiewicz 
70000a2430fSAndrzej Pietrasiewicz fail:
70100a2430fSAndrzej Pietrasiewicz 	if (acm->notify_req)
70200a2430fSAndrzej Pietrasiewicz 		gs_free_req(acm->notify, acm->notify_req);
70300a2430fSAndrzej Pietrasiewicz 
70400a2430fSAndrzej Pietrasiewicz 	ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
70500a2430fSAndrzej Pietrasiewicz 
70600a2430fSAndrzej Pietrasiewicz 	return status;
70700a2430fSAndrzej Pietrasiewicz }
70800a2430fSAndrzej Pietrasiewicz 
acm_unbind(struct usb_configuration * c,struct usb_function * f)70900a2430fSAndrzej Pietrasiewicz static void acm_unbind(struct usb_configuration *c, struct usb_function *f)
71000a2430fSAndrzej Pietrasiewicz {
71100a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = func_to_acm(f);
71200a2430fSAndrzej Pietrasiewicz 
71300a2430fSAndrzej Pietrasiewicz 	acm_string_defs[0].id = 0;
71400a2430fSAndrzej Pietrasiewicz 	usb_free_all_descriptors(f);
71500a2430fSAndrzej Pietrasiewicz 	if (acm->notify_req)
71600a2430fSAndrzej Pietrasiewicz 		gs_free_req(acm->notify, acm->notify_req);
71700a2430fSAndrzej Pietrasiewicz }
71800a2430fSAndrzej Pietrasiewicz 
acm_free_func(struct usb_function * f)71900a2430fSAndrzej Pietrasiewicz static void acm_free_func(struct usb_function *f)
72000a2430fSAndrzej Pietrasiewicz {
72100a2430fSAndrzej Pietrasiewicz 	struct f_acm		*acm = func_to_acm(f);
72200a2430fSAndrzej Pietrasiewicz 
72300a2430fSAndrzej Pietrasiewicz 	kfree(acm);
72400a2430fSAndrzej Pietrasiewicz }
72500a2430fSAndrzej Pietrasiewicz 
acm_resume(struct usb_function * f)7263affccddSFabrice Gasnier static void acm_resume(struct usb_function *f)
7273affccddSFabrice Gasnier {
7283affccddSFabrice Gasnier 	struct f_acm *acm = func_to_acm(f);
7293affccddSFabrice Gasnier 
7303affccddSFabrice Gasnier 	gserial_resume(&acm->port);
7313affccddSFabrice Gasnier }
7323affccddSFabrice Gasnier 
acm_suspend(struct usb_function * f)7333affccddSFabrice Gasnier static void acm_suspend(struct usb_function *f)
7343affccddSFabrice Gasnier {
7353affccddSFabrice Gasnier 	struct f_acm *acm = func_to_acm(f);
7363affccddSFabrice Gasnier 
7373affccddSFabrice Gasnier 	gserial_suspend(&acm->port);
7383affccddSFabrice Gasnier }
7393affccddSFabrice Gasnier 
acm_alloc_func(struct usb_function_instance * fi)74000a2430fSAndrzej Pietrasiewicz static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
74100a2430fSAndrzej Pietrasiewicz {
74200a2430fSAndrzej Pietrasiewicz 	struct f_serial_opts *opts;
74300a2430fSAndrzej Pietrasiewicz 	struct f_acm *acm;
74400a2430fSAndrzej Pietrasiewicz 
74500a2430fSAndrzej Pietrasiewicz 	acm = kzalloc(sizeof(*acm), GFP_KERNEL);
74600a2430fSAndrzej Pietrasiewicz 	if (!acm)
74700a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
74800a2430fSAndrzej Pietrasiewicz 
74900a2430fSAndrzej Pietrasiewicz 	spin_lock_init(&acm->lock);
75000a2430fSAndrzej Pietrasiewicz 
75100a2430fSAndrzej Pietrasiewicz 	acm->port.connect = acm_connect;
75200a2430fSAndrzej Pietrasiewicz 	acm->port.disconnect = acm_disconnect;
75300a2430fSAndrzej Pietrasiewicz 	acm->port.send_break = acm_send_break;
75400a2430fSAndrzej Pietrasiewicz 
75500a2430fSAndrzej Pietrasiewicz 	acm->port.func.name = "acm";
75600a2430fSAndrzej Pietrasiewicz 	acm->port.func.strings = acm_strings;
75700a2430fSAndrzej Pietrasiewicz 	/* descriptors are per-instance copies */
75800a2430fSAndrzej Pietrasiewicz 	acm->port.func.bind = acm_bind;
75900a2430fSAndrzej Pietrasiewicz 	acm->port.func.set_alt = acm_set_alt;
76000a2430fSAndrzej Pietrasiewicz 	acm->port.func.setup = acm_setup;
76100a2430fSAndrzej Pietrasiewicz 	acm->port.func.disable = acm_disable;
76200a2430fSAndrzej Pietrasiewicz 
76300a2430fSAndrzej Pietrasiewicz 	opts = container_of(fi, struct f_serial_opts, func_inst);
76400a2430fSAndrzej Pietrasiewicz 	acm->port_num = opts->port_num;
76500a2430fSAndrzej Pietrasiewicz 	acm->port.func.unbind = acm_unbind;
76600a2430fSAndrzej Pietrasiewicz 	acm->port.func.free_func = acm_free_func;
7673affccddSFabrice Gasnier 	acm->port.func.resume = acm_resume;
7683affccddSFabrice Gasnier 	acm->port.func.suspend = acm_suspend;
76900a2430fSAndrzej Pietrasiewicz 
77000a2430fSAndrzej Pietrasiewicz 	return &acm->port.func;
77100a2430fSAndrzej Pietrasiewicz }
77200a2430fSAndrzej Pietrasiewicz 
to_f_serial_opts(struct config_item * item)77300a2430fSAndrzej Pietrasiewicz static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
77400a2430fSAndrzej Pietrasiewicz {
77500a2430fSAndrzej Pietrasiewicz 	return container_of(to_config_group(item), struct f_serial_opts,
77600a2430fSAndrzej Pietrasiewicz 			func_inst.group);
77700a2430fSAndrzej Pietrasiewicz }
77800a2430fSAndrzej Pietrasiewicz 
acm_attr_release(struct config_item * item)77900a2430fSAndrzej Pietrasiewicz static void acm_attr_release(struct config_item *item)
78000a2430fSAndrzej Pietrasiewicz {
78100a2430fSAndrzej Pietrasiewicz 	struct f_serial_opts *opts = to_f_serial_opts(item);
78200a2430fSAndrzej Pietrasiewicz 
78300a2430fSAndrzej Pietrasiewicz 	usb_put_function_instance(&opts->func_inst);
78400a2430fSAndrzej Pietrasiewicz }
78500a2430fSAndrzej Pietrasiewicz 
78600a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations acm_item_ops = {
78700a2430fSAndrzej Pietrasiewicz 	.release                = acm_attr_release,
78800a2430fSAndrzej Pietrasiewicz };
78900a2430fSAndrzej Pietrasiewicz 
790d7cb8fb7SMichał Mirosław #ifdef CONFIG_U_SERIAL_CONSOLE
791d7cb8fb7SMichał Mirosław 
f_acm_console_store(struct config_item * item,const char * page,size_t count)792d7cb8fb7SMichał Mirosław static ssize_t f_acm_console_store(struct config_item *item,
793d7cb8fb7SMichał Mirosław 		const char *page, size_t count)
794d7cb8fb7SMichał Mirosław {
795d7cb8fb7SMichał Mirosław 	return gserial_set_console(to_f_serial_opts(item)->port_num,
796d7cb8fb7SMichał Mirosław 				   page, count);
797d7cb8fb7SMichał Mirosław }
798d7cb8fb7SMichał Mirosław 
f_acm_console_show(struct config_item * item,char * page)799d7cb8fb7SMichał Mirosław static ssize_t f_acm_console_show(struct config_item *item, char *page)
800d7cb8fb7SMichał Mirosław {
801d7cb8fb7SMichał Mirosław 	return gserial_get_console(to_f_serial_opts(item)->port_num, page);
802d7cb8fb7SMichał Mirosław }
803d7cb8fb7SMichał Mirosław 
804d7cb8fb7SMichał Mirosław CONFIGFS_ATTR(f_acm_, console);
805d7cb8fb7SMichał Mirosław 
806d7cb8fb7SMichał Mirosław #endif /* CONFIG_U_SERIAL_CONSOLE */
807d7cb8fb7SMichał Mirosław 
f_acm_port_num_show(struct config_item * item,char * page)808ea6bd6b1SChristoph Hellwig static ssize_t f_acm_port_num_show(struct config_item *item, char *page)
80900a2430fSAndrzej Pietrasiewicz {
810ea6bd6b1SChristoph Hellwig 	return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
81100a2430fSAndrzej Pietrasiewicz }
81200a2430fSAndrzej Pietrasiewicz 
8130561f77eSKrzysztof Opasiak CONFIGFS_ATTR_RO(f_acm_, port_num);
81400a2430fSAndrzej Pietrasiewicz 
81500a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *acm_attrs[] = {
816d7cb8fb7SMichał Mirosław #ifdef CONFIG_U_SERIAL_CONSOLE
817d7cb8fb7SMichał Mirosław 	&f_acm_attr_console,
818d7cb8fb7SMichał Mirosław #endif
8190561f77eSKrzysztof Opasiak 	&f_acm_attr_port_num,
82000a2430fSAndrzej Pietrasiewicz 	NULL,
82100a2430fSAndrzej Pietrasiewicz };
82200a2430fSAndrzej Pietrasiewicz 
82397363902SBhumika Goyal static const struct config_item_type acm_func_type = {
82400a2430fSAndrzej Pietrasiewicz 	.ct_item_ops    = &acm_item_ops,
82500a2430fSAndrzej Pietrasiewicz 	.ct_attrs	= acm_attrs,
82600a2430fSAndrzej Pietrasiewicz 	.ct_owner       = THIS_MODULE,
82700a2430fSAndrzej Pietrasiewicz };
82800a2430fSAndrzej Pietrasiewicz 
acm_free_instance(struct usb_function_instance * fi)82900a2430fSAndrzej Pietrasiewicz static void acm_free_instance(struct usb_function_instance *fi)
83000a2430fSAndrzej Pietrasiewicz {
83100a2430fSAndrzej Pietrasiewicz 	struct f_serial_opts *opts;
83200a2430fSAndrzej Pietrasiewicz 
83300a2430fSAndrzej Pietrasiewicz 	opts = container_of(fi, struct f_serial_opts, func_inst);
83400a2430fSAndrzej Pietrasiewicz 	gserial_free_line(opts->port_num);
83500a2430fSAndrzej Pietrasiewicz 	kfree(opts);
83600a2430fSAndrzej Pietrasiewicz }
83700a2430fSAndrzej Pietrasiewicz 
acm_alloc_instance(void)83800a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *acm_alloc_instance(void)
83900a2430fSAndrzej Pietrasiewicz {
84000a2430fSAndrzej Pietrasiewicz 	struct f_serial_opts *opts;
84100a2430fSAndrzej Pietrasiewicz 	int ret;
84200a2430fSAndrzej Pietrasiewicz 
84300a2430fSAndrzej Pietrasiewicz 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
84400a2430fSAndrzej Pietrasiewicz 	if (!opts)
84500a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(-ENOMEM);
84600a2430fSAndrzej Pietrasiewicz 	opts->func_inst.free_func_inst = acm_free_instance;
84700a2430fSAndrzej Pietrasiewicz 	ret = gserial_alloc_line(&opts->port_num);
84800a2430fSAndrzej Pietrasiewicz 	if (ret) {
84900a2430fSAndrzej Pietrasiewicz 		kfree(opts);
85000a2430fSAndrzej Pietrasiewicz 		return ERR_PTR(ret);
85100a2430fSAndrzej Pietrasiewicz 	}
85200a2430fSAndrzej Pietrasiewicz 	config_group_init_type_name(&opts->func_inst.group, "",
85300a2430fSAndrzej Pietrasiewicz 			&acm_func_type);
85400a2430fSAndrzej Pietrasiewicz 	return &opts->func_inst;
85500a2430fSAndrzej Pietrasiewicz }
85600a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
85700a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
858