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