162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_acm.c -- USB CDC serial (ACM) function driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) 662306a36Sopenharmony_ci * Copyright (C) 2008 by David Brownell 762306a36Sopenharmony_ci * Copyright (C) 2008 by Nokia Corporation 862306a36Sopenharmony_ci * Copyright (C) 2009 by Samsung Electronics 962306a36Sopenharmony_ci * Author: Michal Nazarewicz (mina86@mina86.com) 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* #define VERBOSE_DEBUG */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include <linux/err.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "u_serial.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * This CDC ACM function support just wraps control functions and 2562306a36Sopenharmony_ci * notifications around the generic serial-over-usb code. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * Because CDC ACM is standardized by the USB-IF, many host operating 2862306a36Sopenharmony_ci * systems have drivers for it. Accordingly, ACM is the preferred 2962306a36Sopenharmony_ci * interop solution for serial-port type connections. The control 3062306a36Sopenharmony_ci * models are often not necessary, and in any case don't do much in 3162306a36Sopenharmony_ci * this bare-bones implementation. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Note that even MS-Windows has some support for ACM. However, that 3462306a36Sopenharmony_ci * support is somewhat broken because when you use ACM in a composite 3562306a36Sopenharmony_ci * device, having multiple interfaces confuses the poor OS. It doesn't 3662306a36Sopenharmony_ci * seem to understand CDC Union descriptors. The new "association" 3762306a36Sopenharmony_ci * descriptors (roughly equivalent to CDC Unions) may sometimes help. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct f_acm { 4162306a36Sopenharmony_ci struct gserial port; 4262306a36Sopenharmony_ci u8 ctrl_id, data_id; 4362306a36Sopenharmony_ci u8 port_num; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci u8 pending; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* lock is mostly for pending and notify_req ... they get accessed 4862306a36Sopenharmony_ci * by callbacks both from tty (open/close/break) under its spinlock, 4962306a36Sopenharmony_ci * and notify_req.complete() which can't use that lock. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci spinlock_t lock; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci struct usb_ep *notify; 5462306a36Sopenharmony_ci struct usb_request *notify_req; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */ 5962306a36Sopenharmony_ci u16 port_handshake_bits; 6062306a36Sopenharmony_ci /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */ 6162306a36Sopenharmony_ci u16 serial_state; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic inline struct f_acm *func_to_acm(struct usb_function *f) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci return container_of(f, struct f_acm, port.func); 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic inline struct f_acm *port_to_acm(struct gserial *p) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci return container_of(p, struct f_acm, port); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* notification endpoint uses smallish and infrequent fixed-size messages */ 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define GS_NOTIFY_INTERVAL_MS 32 7962306a36Sopenharmony_ci#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* interface and class descriptors: */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic struct usb_interface_assoc_descriptor 8462306a36Sopenharmony_ciacm_iad_descriptor = { 8562306a36Sopenharmony_ci .bLength = sizeof acm_iad_descriptor, 8662306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci /* .bFirstInterface = DYNAMIC, */ 8962306a36Sopenharmony_ci .bInterfaceCount = 2, // control + data 9062306a36Sopenharmony_ci .bFunctionClass = USB_CLASS_COMM, 9162306a36Sopenharmony_ci .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, 9262306a36Sopenharmony_ci .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER, 9362306a36Sopenharmony_ci /* .iFunction = DYNAMIC */ 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic struct usb_interface_descriptor acm_control_interface_desc = { 9862306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 9962306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 10062306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC */ 10162306a36Sopenharmony_ci .bNumEndpoints = 1, 10262306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 10362306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, 10462306a36Sopenharmony_ci .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER, 10562306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct usb_interface_descriptor acm_data_interface_desc = { 10962306a36Sopenharmony_ci .bLength = USB_DT_INTERFACE_SIZE, 11062306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 11162306a36Sopenharmony_ci /* .bInterfaceNumber = DYNAMIC */ 11262306a36Sopenharmony_ci .bNumEndpoints = 2, 11362306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 11462306a36Sopenharmony_ci .bInterfaceSubClass = 0, 11562306a36Sopenharmony_ci .bInterfaceProtocol = 0, 11662306a36Sopenharmony_ci /* .iInterface = DYNAMIC */ 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct usb_cdc_header_desc acm_header_desc = { 12062306a36Sopenharmony_ci .bLength = sizeof(acm_header_desc), 12162306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 12262306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_HEADER_TYPE, 12362306a36Sopenharmony_ci .bcdCDC = cpu_to_le16(0x0110), 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic struct usb_cdc_call_mgmt_descriptor 12762306a36Sopenharmony_ciacm_call_mgmt_descriptor = { 12862306a36Sopenharmony_ci .bLength = sizeof(acm_call_mgmt_descriptor), 12962306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 13062306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, 13162306a36Sopenharmony_ci .bmCapabilities = 0, 13262306a36Sopenharmony_ci /* .bDataInterface = DYNAMIC */ 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic struct usb_cdc_acm_descriptor acm_descriptor = { 13662306a36Sopenharmony_ci .bLength = sizeof(acm_descriptor), 13762306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 13862306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_ACM_TYPE, 13962306a36Sopenharmony_ci .bmCapabilities = USB_CDC_CAP_LINE, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic struct usb_cdc_union_desc acm_union_desc = { 14362306a36Sopenharmony_ci .bLength = sizeof(acm_union_desc), 14462306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 14562306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_UNION_TYPE, 14662306a36Sopenharmony_ci /* .bMasterInterface0 = DYNAMIC */ 14762306a36Sopenharmony_ci /* .bSlaveInterface0 = DYNAMIC */ 14862306a36Sopenharmony_ci}; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/* full speed support: */ 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_fs_notify_desc = { 15362306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 15462306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 15562306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 15662306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 15762306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET), 15862306a36Sopenharmony_ci .bInterval = GS_NOTIFY_INTERVAL_MS, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_fs_in_desc = { 16262306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 16362306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 16462306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 16562306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_fs_out_desc = { 16962306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 17062306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 17162306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 17262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic struct usb_descriptor_header *acm_fs_function[] = { 17662306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_iad_descriptor, 17762306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_control_interface_desc, 17862306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_header_desc, 17962306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, 18062306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_descriptor, 18162306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_union_desc, 18262306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_fs_notify_desc, 18362306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_data_interface_desc, 18462306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_fs_in_desc, 18562306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_fs_out_desc, 18662306a36Sopenharmony_ci NULL, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* high speed support: */ 19062306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_hs_notify_desc = { 19162306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 19262306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 19362306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 19462306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_INT, 19562306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET), 19662306a36Sopenharmony_ci .bInterval = USB_MS_TO_HS_INTERVAL(GS_NOTIFY_INTERVAL_MS), 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_hs_in_desc = { 20062306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 20162306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 20262306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 20362306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_hs_out_desc = { 20762306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 20862306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 20962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 21062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 21162306a36Sopenharmony_ci}; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic struct usb_descriptor_header *acm_hs_function[] = { 21462306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_iad_descriptor, 21562306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_control_interface_desc, 21662306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_header_desc, 21762306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, 21862306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_descriptor, 21962306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_union_desc, 22062306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_hs_notify_desc, 22162306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_data_interface_desc, 22262306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_hs_in_desc, 22362306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_hs_out_desc, 22462306a36Sopenharmony_ci NULL, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_ss_in_desc = { 22862306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 22962306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 23062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 23162306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor acm_ss_out_desc = { 23562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 23662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 23762306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 23862306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(1024), 23962306a36Sopenharmony_ci}; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = { 24262306a36Sopenharmony_ci .bLength = sizeof acm_ss_bulk_comp_desc, 24362306a36Sopenharmony_ci .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct usb_descriptor_header *acm_ss_function[] = { 24762306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_iad_descriptor, 24862306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_control_interface_desc, 24962306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_header_desc, 25062306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_call_mgmt_descriptor, 25162306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_descriptor, 25262306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_union_desc, 25362306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_hs_notify_desc, 25462306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, 25562306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_data_interface_desc, 25662306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_ss_in_desc, 25762306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, 25862306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_ss_out_desc, 25962306a36Sopenharmony_ci (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc, 26062306a36Sopenharmony_ci NULL, 26162306a36Sopenharmony_ci}; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci/* string descriptors: */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci#define ACM_CTRL_IDX 0 26662306a36Sopenharmony_ci#define ACM_DATA_IDX 1 26762306a36Sopenharmony_ci#define ACM_IAD_IDX 2 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/* static strings, in UTF-8 */ 27062306a36Sopenharmony_cistatic struct usb_string acm_string_defs[] = { 27162306a36Sopenharmony_ci [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)", 27262306a36Sopenharmony_ci [ACM_DATA_IDX].s = "CDC ACM Data", 27362306a36Sopenharmony_ci [ACM_IAD_IDX ].s = "CDC Serial", 27462306a36Sopenharmony_ci { } /* end of list */ 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic struct usb_gadget_strings acm_string_table = { 27862306a36Sopenharmony_ci .language = 0x0409, /* en-us */ 27962306a36Sopenharmony_ci .strings = acm_string_defs, 28062306a36Sopenharmony_ci}; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic struct usb_gadget_strings *acm_strings[] = { 28362306a36Sopenharmony_ci &acm_string_table, 28462306a36Sopenharmony_ci NULL, 28562306a36Sopenharmony_ci}; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/* ACM control ... data handling is delegated to tty library code. 29062306a36Sopenharmony_ci * The main task of this function is to activate and deactivate 29162306a36Sopenharmony_ci * that code based on device state; track parameters like line 29262306a36Sopenharmony_ci * speed, handshake state, and so on; and issue notifications. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic void acm_complete_set_line_coding(struct usb_ep *ep, 29662306a36Sopenharmony_ci struct usb_request *req) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct f_acm *acm = ep->driver_data; 29962306a36Sopenharmony_ci struct usb_composite_dev *cdev = acm->port.func.config->cdev; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (req->status != 0) { 30262306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, "acm ttyGS%d completion, err %d\n", 30362306a36Sopenharmony_ci acm->port_num, req->status); 30462306a36Sopenharmony_ci return; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* normal completion */ 30862306a36Sopenharmony_ci if (req->actual != sizeof(acm->port_line_coding)) { 30962306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, "acm ttyGS%d short resp, len %d\n", 31062306a36Sopenharmony_ci acm->port_num, req->actual); 31162306a36Sopenharmony_ci usb_ep_set_halt(ep); 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci struct usb_cdc_line_coding *value = req->buf; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* REVISIT: we currently just remember this data. 31662306a36Sopenharmony_ci * If we change that, (a) validate it first, then 31762306a36Sopenharmony_ci * (b) update whatever hardware needs updating, 31862306a36Sopenharmony_ci * (c) worry about locking. This is information on 31962306a36Sopenharmony_ci * the order of 9600-8-N-1 ... most of which means 32062306a36Sopenharmony_ci * nothing unless we control a real RS232 line. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_ci acm->port_line_coding = *value; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int acm_send_break(struct gserial *port, int duration); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 33162306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 33262306a36Sopenharmony_ci struct usb_request *req = cdev->req; 33362306a36Sopenharmony_ci int value = -EOPNOTSUPP; 33462306a36Sopenharmony_ci u16 w_index = le16_to_cpu(ctrl->wIndex); 33562306a36Sopenharmony_ci u16 w_value = le16_to_cpu(ctrl->wValue); 33662306a36Sopenharmony_ci u16 w_length = le16_to_cpu(ctrl->wLength); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* composite driver infrastructure handles everything except 33962306a36Sopenharmony_ci * CDC class messages; interface activation uses set_alt(). 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * Note CDC spec table 4 lists the ACM request profile. It requires 34262306a36Sopenharmony_ci * encapsulated command support ... we don't handle any, and respond 34362306a36Sopenharmony_ci * to them by stalling. Options include get/set/clear comm features 34462306a36Sopenharmony_ci * (not that useful) and SEND_BREAK. 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* SET_LINE_CODING ... just read and save what the host sends */ 34962306a36Sopenharmony_ci case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) 35062306a36Sopenharmony_ci | USB_CDC_REQ_SET_LINE_CODING: 35162306a36Sopenharmony_ci if (w_length != sizeof(struct usb_cdc_line_coding) 35262306a36Sopenharmony_ci || w_index != acm->ctrl_id) 35362306a36Sopenharmony_ci goto invalid; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci value = w_length; 35662306a36Sopenharmony_ci cdev->gadget->ep0->driver_data = acm; 35762306a36Sopenharmony_ci req->complete = acm_complete_set_line_coding; 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* GET_LINE_CODING ... return what host sent, or initial value */ 36162306a36Sopenharmony_ci case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) 36262306a36Sopenharmony_ci | USB_CDC_REQ_GET_LINE_CODING: 36362306a36Sopenharmony_ci if (w_index != acm->ctrl_id) 36462306a36Sopenharmony_ci goto invalid; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci value = min_t(unsigned, w_length, 36762306a36Sopenharmony_ci sizeof(struct usb_cdc_line_coding)); 36862306a36Sopenharmony_ci memcpy(req->buf, &acm->port_line_coding, value); 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* SET_CONTROL_LINE_STATE ... save what the host sent */ 37262306a36Sopenharmony_ci case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) 37362306a36Sopenharmony_ci | USB_CDC_REQ_SET_CONTROL_LINE_STATE: 37462306a36Sopenharmony_ci if (w_index != acm->ctrl_id) 37562306a36Sopenharmony_ci goto invalid; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci value = 0; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* FIXME we should not allow data to flow until the 38062306a36Sopenharmony_ci * host sets the USB_CDC_CTRL_DTR bit; and when it clears 38162306a36Sopenharmony_ci * that bit, we should return to that no-flow state. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci acm->port_handshake_bits = w_value; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) 38762306a36Sopenharmony_ci | USB_CDC_REQ_SEND_BREAK: 38862306a36Sopenharmony_ci if (w_index != acm->ctrl_id) 38962306a36Sopenharmony_ci goto invalid; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci acm_send_break(&acm->port, w_value); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci default: 39562306a36Sopenharmony_ciinvalid: 39662306a36Sopenharmony_ci dev_vdbg(&cdev->gadget->dev, 39762306a36Sopenharmony_ci "invalid control req%02x.%02x v%04x i%04x l%d\n", 39862306a36Sopenharmony_ci ctrl->bRequestType, ctrl->bRequest, 39962306a36Sopenharmony_ci w_value, w_index, w_length); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* respond with data transfer or status phase? */ 40362306a36Sopenharmony_ci if (value >= 0) { 40462306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 40562306a36Sopenharmony_ci "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n", 40662306a36Sopenharmony_ci acm->port_num, ctrl->bRequestType, ctrl->bRequest, 40762306a36Sopenharmony_ci w_value, w_index, w_length); 40862306a36Sopenharmony_ci req->zero = 0; 40962306a36Sopenharmony_ci req->length = value; 41062306a36Sopenharmony_ci value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); 41162306a36Sopenharmony_ci if (value < 0) 41262306a36Sopenharmony_ci ERROR(cdev, "acm response on ttyGS%d, err %d\n", 41362306a36Sopenharmony_ci acm->port_num, value); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* device either stalls (value < 0) or reports success */ 41762306a36Sopenharmony_ci return value; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 42362306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* we know alt == 0, so this is an activation or a reset */ 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (intf == acm->ctrl_id) { 42862306a36Sopenharmony_ci if (acm->notify->enabled) { 42962306a36Sopenharmony_ci dev_vdbg(&cdev->gadget->dev, 43062306a36Sopenharmony_ci "reset acm control interface %d\n", intf); 43162306a36Sopenharmony_ci usb_ep_disable(acm->notify); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (!acm->notify->desc) 43562306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, acm->notify)) 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci usb_ep_enable(acm->notify); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci } else if (intf == acm->data_id) { 44162306a36Sopenharmony_ci if (acm->notify->enabled) { 44262306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 44362306a36Sopenharmony_ci "reset acm ttyGS%d\n", acm->port_num); 44462306a36Sopenharmony_ci gserial_disconnect(&acm->port); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci if (!acm->port.in->desc || !acm->port.out->desc) { 44762306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 44862306a36Sopenharmony_ci "activate acm ttyGS%d\n", acm->port_num); 44962306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, 45062306a36Sopenharmony_ci acm->port.in) || 45162306a36Sopenharmony_ci config_ep_by_speed(cdev->gadget, f, 45262306a36Sopenharmony_ci acm->port.out)) { 45362306a36Sopenharmony_ci acm->port.in->desc = NULL; 45462306a36Sopenharmony_ci acm->port.out->desc = NULL; 45562306a36Sopenharmony_ci return -EINVAL; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci gserial_connect(&acm->port, acm->port_num); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci } else 46162306a36Sopenharmony_ci return -EINVAL; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic void acm_disable(struct usb_function *f) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 46962306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num); 47262306a36Sopenharmony_ci gserial_disconnect(&acm->port); 47362306a36Sopenharmony_ci usb_ep_disable(acm->notify); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/** 47962306a36Sopenharmony_ci * acm_cdc_notify - issue CDC notification to host 48062306a36Sopenharmony_ci * @acm: wraps host to be notified 48162306a36Sopenharmony_ci * @type: notification type 48262306a36Sopenharmony_ci * @value: Refer to cdc specs, wValue field. 48362306a36Sopenharmony_ci * @data: data to be sent 48462306a36Sopenharmony_ci * @length: size of data 48562306a36Sopenharmony_ci * Context: irqs blocked, acm->lock held, acm_notify_req non-null 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * Returns zero on success or a negative errno. 48862306a36Sopenharmony_ci * 48962306a36Sopenharmony_ci * See section 6.3.5 of the CDC 1.1 specification for information 49062306a36Sopenharmony_ci * about the only notification we issue: SerialState change. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_cistatic int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value, 49362306a36Sopenharmony_ci void *data, unsigned length) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct usb_ep *ep = acm->notify; 49662306a36Sopenharmony_ci struct usb_request *req; 49762306a36Sopenharmony_ci struct usb_cdc_notification *notify; 49862306a36Sopenharmony_ci const unsigned len = sizeof(*notify) + length; 49962306a36Sopenharmony_ci void *buf; 50062306a36Sopenharmony_ci int status; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci req = acm->notify_req; 50362306a36Sopenharmony_ci acm->notify_req = NULL; 50462306a36Sopenharmony_ci acm->pending = false; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci req->length = len; 50762306a36Sopenharmony_ci notify = req->buf; 50862306a36Sopenharmony_ci buf = notify + 1; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS 51162306a36Sopenharmony_ci | USB_RECIP_INTERFACE; 51262306a36Sopenharmony_ci notify->bNotificationType = type; 51362306a36Sopenharmony_ci notify->wValue = cpu_to_le16(value); 51462306a36Sopenharmony_ci notify->wIndex = cpu_to_le16(acm->ctrl_id); 51562306a36Sopenharmony_ci notify->wLength = cpu_to_le16(length); 51662306a36Sopenharmony_ci memcpy(buf, data, length); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* ep_queue() can complete immediately if it fills the fifo... */ 51962306a36Sopenharmony_ci spin_unlock(&acm->lock); 52062306a36Sopenharmony_ci status = usb_ep_queue(ep, req, GFP_ATOMIC); 52162306a36Sopenharmony_ci spin_lock(&acm->lock); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (status < 0) { 52462306a36Sopenharmony_ci ERROR(acm->port.func.config->cdev, 52562306a36Sopenharmony_ci "acm ttyGS%d can't notify serial state, %d\n", 52662306a36Sopenharmony_ci acm->port_num, status); 52762306a36Sopenharmony_ci acm->notify_req = req; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return status; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic int acm_notify_serial_state(struct f_acm *acm) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci struct usb_composite_dev *cdev = acm->port.func.config->cdev; 53662306a36Sopenharmony_ci int status; 53762306a36Sopenharmony_ci __le16 serial_state; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci spin_lock(&acm->lock); 54062306a36Sopenharmony_ci if (acm->notify_req) { 54162306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, "acm ttyGS%d serial state %04x\n", 54262306a36Sopenharmony_ci acm->port_num, acm->serial_state); 54362306a36Sopenharmony_ci serial_state = cpu_to_le16(acm->serial_state); 54462306a36Sopenharmony_ci status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE, 54562306a36Sopenharmony_ci 0, &serial_state, sizeof(acm->serial_state)); 54662306a36Sopenharmony_ci } else { 54762306a36Sopenharmony_ci acm->pending = true; 54862306a36Sopenharmony_ci status = 0; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci spin_unlock(&acm->lock); 55162306a36Sopenharmony_ci return status; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct f_acm *acm = req->context; 55762306a36Sopenharmony_ci u8 doit = false; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* on this call path we do NOT hold the port spinlock, 56062306a36Sopenharmony_ci * which is why ACM needs its own spinlock 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci spin_lock(&acm->lock); 56362306a36Sopenharmony_ci if (req->status != -ESHUTDOWN) 56462306a36Sopenharmony_ci doit = acm->pending; 56562306a36Sopenharmony_ci acm->notify_req = req; 56662306a36Sopenharmony_ci spin_unlock(&acm->lock); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (doit) 56962306a36Sopenharmony_ci acm_notify_serial_state(acm); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* connect == the TTY link is open */ 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic void acm_connect(struct gserial *port) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct f_acm *acm = port_to_acm(port); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci acm->serial_state |= USB_CDC_SERIAL_STATE_DSR | USB_CDC_SERIAL_STATE_DCD; 57962306a36Sopenharmony_ci acm_notify_serial_state(acm); 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void acm_disconnect(struct gserial *port) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct f_acm *acm = port_to_acm(port); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci acm->serial_state &= ~(USB_CDC_SERIAL_STATE_DSR | USB_CDC_SERIAL_STATE_DCD); 58762306a36Sopenharmony_ci acm_notify_serial_state(acm); 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int acm_send_break(struct gserial *port, int duration) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct f_acm *acm = port_to_acm(port); 59362306a36Sopenharmony_ci u16 state; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci state = acm->serial_state; 59662306a36Sopenharmony_ci state &= ~USB_CDC_SERIAL_STATE_BREAK; 59762306a36Sopenharmony_ci if (duration) 59862306a36Sopenharmony_ci state |= USB_CDC_SERIAL_STATE_BREAK; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci acm->serial_state = state; 60162306a36Sopenharmony_ci return acm_notify_serial_state(acm); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci/* ACM function driver setup/binding */ 60762306a36Sopenharmony_cistatic int 60862306a36Sopenharmony_ciacm_bind(struct usb_configuration *c, struct usb_function *f) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 61162306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 61262306a36Sopenharmony_ci struct usb_string *us; 61362306a36Sopenharmony_ci int status; 61462306a36Sopenharmony_ci struct usb_ep *ep; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci /* REVISIT might want instance-specific strings to help 61762306a36Sopenharmony_ci * distinguish instances ... 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* maybe allocate device-global string IDs, and patch descriptors */ 62162306a36Sopenharmony_ci us = usb_gstrings_attach(cdev, acm_strings, 62262306a36Sopenharmony_ci ARRAY_SIZE(acm_string_defs)); 62362306a36Sopenharmony_ci if (IS_ERR(us)) 62462306a36Sopenharmony_ci return PTR_ERR(us); 62562306a36Sopenharmony_ci acm_control_interface_desc.iInterface = us[ACM_CTRL_IDX].id; 62662306a36Sopenharmony_ci acm_data_interface_desc.iInterface = us[ACM_DATA_IDX].id; 62762306a36Sopenharmony_ci acm_iad_descriptor.iFunction = us[ACM_IAD_IDX].id; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci /* allocate instance-specific interface IDs, and patch descriptors */ 63062306a36Sopenharmony_ci status = usb_interface_id(c, f); 63162306a36Sopenharmony_ci if (status < 0) 63262306a36Sopenharmony_ci goto fail; 63362306a36Sopenharmony_ci acm->ctrl_id = status; 63462306a36Sopenharmony_ci acm_iad_descriptor.bFirstInterface = status; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci acm_control_interface_desc.bInterfaceNumber = status; 63762306a36Sopenharmony_ci acm_union_desc .bMasterInterface0 = status; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci status = usb_interface_id(c, f); 64062306a36Sopenharmony_ci if (status < 0) 64162306a36Sopenharmony_ci goto fail; 64262306a36Sopenharmony_ci acm->data_id = status; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci acm_data_interface_desc.bInterfaceNumber = status; 64562306a36Sopenharmony_ci acm_union_desc.bSlaveInterface0 = status; 64662306a36Sopenharmony_ci acm_call_mgmt_descriptor.bDataInterface = status; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci status = -ENODEV; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 65162306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc); 65262306a36Sopenharmony_ci if (!ep) 65362306a36Sopenharmony_ci goto fail; 65462306a36Sopenharmony_ci acm->port.in = ep; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); 65762306a36Sopenharmony_ci if (!ep) 65862306a36Sopenharmony_ci goto fail; 65962306a36Sopenharmony_ci acm->port.out = ep; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); 66262306a36Sopenharmony_ci if (!ep) 66362306a36Sopenharmony_ci goto fail; 66462306a36Sopenharmony_ci acm->notify = ep; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* allocate notification */ 66762306a36Sopenharmony_ci acm->notify_req = gs_alloc_req(ep, 66862306a36Sopenharmony_ci sizeof(struct usb_cdc_notification) + 2, 66962306a36Sopenharmony_ci GFP_KERNEL); 67062306a36Sopenharmony_ci if (!acm->notify_req) 67162306a36Sopenharmony_ci goto fail; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci acm->notify_req->complete = acm_cdc_notify_complete; 67462306a36Sopenharmony_ci acm->notify_req->context = acm; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci /* support all relevant hardware speeds... we expect that when 67762306a36Sopenharmony_ci * hardware is dual speed, all bulk-capable endpoints work at 67862306a36Sopenharmony_ci * both speeds 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_ci acm_hs_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; 68162306a36Sopenharmony_ci acm_hs_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; 68262306a36Sopenharmony_ci acm_hs_notify_desc.bEndpointAddress = 68362306a36Sopenharmony_ci acm_fs_notify_desc.bEndpointAddress; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci acm_ss_in_desc.bEndpointAddress = acm_fs_in_desc.bEndpointAddress; 68662306a36Sopenharmony_ci acm_ss_out_desc.bEndpointAddress = acm_fs_out_desc.bEndpointAddress; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci status = usb_assign_descriptors(f, acm_fs_function, acm_hs_function, 68962306a36Sopenharmony_ci acm_ss_function, acm_ss_function); 69062306a36Sopenharmony_ci if (status) 69162306a36Sopenharmony_ci goto fail; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 69462306a36Sopenharmony_ci "acm ttyGS%d: IN/%s OUT/%s NOTIFY/%s\n", 69562306a36Sopenharmony_ci acm->port_num, 69662306a36Sopenharmony_ci acm->port.in->name, acm->port.out->name, 69762306a36Sopenharmony_ci acm->notify->name); 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cifail: 70162306a36Sopenharmony_ci if (acm->notify_req) 70262306a36Sopenharmony_ci gs_free_req(acm->notify, acm->notify_req); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return status; 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void acm_unbind(struct usb_configuration *c, struct usb_function *f) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci acm_string_defs[0].id = 0; 71462306a36Sopenharmony_ci usb_free_all_descriptors(f); 71562306a36Sopenharmony_ci if (acm->notify_req) 71662306a36Sopenharmony_ci gs_free_req(acm->notify, acm->notify_req); 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic void acm_free_func(struct usb_function *f) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci kfree(acm); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void acm_resume(struct usb_function *f) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci gserial_resume(&acm->port); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void acm_suspend(struct usb_function *f) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct f_acm *acm = func_to_acm(f); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci gserial_suspend(&acm->port); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic struct usb_function *acm_alloc_func(struct usb_function_instance *fi) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct f_serial_opts *opts; 74362306a36Sopenharmony_ci struct f_acm *acm; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci acm = kzalloc(sizeof(*acm), GFP_KERNEL); 74662306a36Sopenharmony_ci if (!acm) 74762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci spin_lock_init(&acm->lock); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci acm->port.connect = acm_connect; 75262306a36Sopenharmony_ci acm->port.disconnect = acm_disconnect; 75362306a36Sopenharmony_ci acm->port.send_break = acm_send_break; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci acm->port.func.name = "acm"; 75662306a36Sopenharmony_ci acm->port.func.strings = acm_strings; 75762306a36Sopenharmony_ci /* descriptors are per-instance copies */ 75862306a36Sopenharmony_ci acm->port.func.bind = acm_bind; 75962306a36Sopenharmony_ci acm->port.func.set_alt = acm_set_alt; 76062306a36Sopenharmony_ci acm->port.func.setup = acm_setup; 76162306a36Sopenharmony_ci acm->port.func.disable = acm_disable; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci opts = container_of(fi, struct f_serial_opts, func_inst); 76462306a36Sopenharmony_ci acm->port_num = opts->port_num; 76562306a36Sopenharmony_ci acm->port.func.unbind = acm_unbind; 76662306a36Sopenharmony_ci acm->port.func.free_func = acm_free_func; 76762306a36Sopenharmony_ci acm->port.func.resume = acm_resume; 76862306a36Sopenharmony_ci acm->port.func.suspend = acm_suspend; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return &acm->port.func; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_serial_opts, 77662306a36Sopenharmony_ci func_inst.group); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cistatic void acm_attr_release(struct config_item *item) 78062306a36Sopenharmony_ci{ 78162306a36Sopenharmony_ci struct f_serial_opts *opts = to_f_serial_opts(item); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci usb_put_function_instance(&opts->func_inst); 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic struct configfs_item_operations acm_item_ops = { 78762306a36Sopenharmony_ci .release = acm_attr_release, 78862306a36Sopenharmony_ci}; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci#ifdef CONFIG_U_SERIAL_CONSOLE 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic ssize_t f_acm_console_store(struct config_item *item, 79362306a36Sopenharmony_ci const char *page, size_t count) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci return gserial_set_console(to_f_serial_opts(item)->port_num, 79662306a36Sopenharmony_ci page, count); 79762306a36Sopenharmony_ci} 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_cistatic ssize_t f_acm_console_show(struct config_item *item, char *page) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci return gserial_get_console(to_f_serial_opts(item)->port_num, page); 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ciCONFIGFS_ATTR(f_acm_, console); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci#endif /* CONFIG_U_SERIAL_CONSOLE */ 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic ssize_t f_acm_port_num_show(struct config_item *item, char *page) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num); 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ciCONFIGFS_ATTR_RO(f_acm_, port_num); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic struct configfs_attribute *acm_attrs[] = { 81662306a36Sopenharmony_ci#ifdef CONFIG_U_SERIAL_CONSOLE 81762306a36Sopenharmony_ci &f_acm_attr_console, 81862306a36Sopenharmony_ci#endif 81962306a36Sopenharmony_ci &f_acm_attr_port_num, 82062306a36Sopenharmony_ci NULL, 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic const struct config_item_type acm_func_type = { 82462306a36Sopenharmony_ci .ct_item_ops = &acm_item_ops, 82562306a36Sopenharmony_ci .ct_attrs = acm_attrs, 82662306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 82762306a36Sopenharmony_ci}; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic void acm_free_instance(struct usb_function_instance *fi) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct f_serial_opts *opts; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci opts = container_of(fi, struct f_serial_opts, func_inst); 83462306a36Sopenharmony_ci gserial_free_line(opts->port_num); 83562306a36Sopenharmony_ci kfree(opts); 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic struct usb_function_instance *acm_alloc_instance(void) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct f_serial_opts *opts; 84162306a36Sopenharmony_ci int ret; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 84462306a36Sopenharmony_ci if (!opts) 84562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 84662306a36Sopenharmony_ci opts->func_inst.free_func_inst = acm_free_instance; 84762306a36Sopenharmony_ci ret = gserial_alloc_line(&opts->port_num); 84862306a36Sopenharmony_ci if (ret) { 84962306a36Sopenharmony_ci kfree(opts); 85062306a36Sopenharmony_ci return ERR_PTR(ret); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", 85362306a36Sopenharmony_ci &acm_func_type); 85462306a36Sopenharmony_ci return &opts->func_inst; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func); 85762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 858