162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * f_obex.c -- USB CDC OBEX function driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 662306a36Sopenharmony_ci * Contact: Felipe Balbi <felipe.balbi@nokia.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Based on f_acm.c by Al Borchers and David Brownell. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* #define VERBOSE_DEBUG */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "u_serial.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * This CDC OBEX function support just packages a TTY-ish byte stream. 2362306a36Sopenharmony_ci * A user mode server will put it into "raw" mode and handle all the 2462306a36Sopenharmony_ci * relevant protocol details ... this is just a kernel passthrough. 2562306a36Sopenharmony_ci * When possible, we prevent gadget enumeration until that server is 2662306a36Sopenharmony_ci * ready to handle the commands. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct f_obex { 3062306a36Sopenharmony_ci struct gserial port; 3162306a36Sopenharmony_ci u8 ctrl_id; 3262306a36Sopenharmony_ci u8 data_id; 3362306a36Sopenharmony_ci u8 cur_alt; 3462306a36Sopenharmony_ci u8 port_num; 3562306a36Sopenharmony_ci}; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic inline struct f_obex *func_to_obex(struct usb_function *f) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci return container_of(f, struct f_obex, port.func); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline struct f_obex *port_to_obex(struct gserial *p) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci return container_of(p, struct f_obex, port); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define OBEX_CTRL_IDX 0 5062306a36Sopenharmony_ci#define OBEX_DATA_IDX 1 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic struct usb_string obex_string_defs[] = { 5362306a36Sopenharmony_ci [OBEX_CTRL_IDX].s = "CDC Object Exchange (OBEX)", 5462306a36Sopenharmony_ci [OBEX_DATA_IDX].s = "CDC OBEX Data", 5562306a36Sopenharmony_ci { }, /* end of list */ 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct usb_gadget_strings obex_string_table = { 5962306a36Sopenharmony_ci .language = 0x0409, /* en-US */ 6062306a36Sopenharmony_ci .strings = obex_string_defs, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct usb_gadget_strings *obex_strings[] = { 6462306a36Sopenharmony_ci &obex_string_table, 6562306a36Sopenharmony_ci NULL, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct usb_interface_descriptor obex_control_intf = { 7162306a36Sopenharmony_ci .bLength = sizeof(obex_control_intf), 7262306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 7362306a36Sopenharmony_ci .bInterfaceNumber = 0, 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci .bAlternateSetting = 0, 7662306a36Sopenharmony_ci .bNumEndpoints = 0, 7762306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 7862306a36Sopenharmony_ci .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX, 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic struct usb_interface_descriptor obex_data_nop_intf = { 8262306a36Sopenharmony_ci .bLength = sizeof(obex_data_nop_intf), 8362306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 8462306a36Sopenharmony_ci .bInterfaceNumber = 1, 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci .bAlternateSetting = 0, 8762306a36Sopenharmony_ci .bNumEndpoints = 0, 8862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic struct usb_interface_descriptor obex_data_intf = { 9262306a36Sopenharmony_ci .bLength = sizeof(obex_data_intf), 9362306a36Sopenharmony_ci .bDescriptorType = USB_DT_INTERFACE, 9462306a36Sopenharmony_ci .bInterfaceNumber = 2, 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci .bAlternateSetting = 1, 9762306a36Sopenharmony_ci .bNumEndpoints = 2, 9862306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_CDC_DATA, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic struct usb_cdc_header_desc obex_cdc_header_desc = { 10262306a36Sopenharmony_ci .bLength = sizeof(obex_cdc_header_desc), 10362306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 10462306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_HEADER_TYPE, 10562306a36Sopenharmony_ci .bcdCDC = cpu_to_le16(0x0120), 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic struct usb_cdc_union_desc obex_cdc_union_desc = { 10962306a36Sopenharmony_ci .bLength = sizeof(obex_cdc_union_desc), 11062306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 11162306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_UNION_TYPE, 11262306a36Sopenharmony_ci .bMasterInterface0 = 1, 11362306a36Sopenharmony_ci .bSlaveInterface0 = 2, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic struct usb_cdc_obex_desc obex_desc = { 11762306a36Sopenharmony_ci .bLength = sizeof(obex_desc), 11862306a36Sopenharmony_ci .bDescriptorType = USB_DT_CS_INTERFACE, 11962306a36Sopenharmony_ci .bDescriptorSubType = USB_CDC_OBEX_TYPE, 12062306a36Sopenharmony_ci .bcdVersion = cpu_to_le16(0x0100), 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* High-Speed Support */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic struct usb_endpoint_descriptor obex_hs_ep_out_desc = { 12662306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 12762306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 13062306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 13162306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor obex_hs_ep_in_desc = { 13562306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 13662306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 13962306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 14062306a36Sopenharmony_ci .wMaxPacketSize = cpu_to_le16(512), 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic struct usb_descriptor_header *hs_function[] = { 14462306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_control_intf, 14562306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_cdc_header_desc, 14662306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_desc, 14762306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_cdc_union_desc, 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_data_nop_intf, 15062306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_data_intf, 15162306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_hs_ep_in_desc, 15262306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_hs_ep_out_desc, 15362306a36Sopenharmony_ci NULL, 15462306a36Sopenharmony_ci}; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/* Full-Speed Support */ 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic struct usb_endpoint_descriptor obex_fs_ep_in_desc = { 15962306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 16062306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_IN, 16362306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 16462306a36Sopenharmony_ci}; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct usb_endpoint_descriptor obex_fs_ep_out_desc = { 16762306a36Sopenharmony_ci .bLength = USB_DT_ENDPOINT_SIZE, 16862306a36Sopenharmony_ci .bDescriptorType = USB_DT_ENDPOINT, 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci .bEndpointAddress = USB_DIR_OUT, 17162306a36Sopenharmony_ci .bmAttributes = USB_ENDPOINT_XFER_BULK, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct usb_descriptor_header *fs_function[] = { 17562306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_control_intf, 17662306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_cdc_header_desc, 17762306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_desc, 17862306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_cdc_union_desc, 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_data_nop_intf, 18162306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_data_intf, 18262306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_fs_ep_in_desc, 18362306a36Sopenharmony_ci (struct usb_descriptor_header *) &obex_fs_ep_out_desc, 18462306a36Sopenharmony_ci NULL, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct f_obex *obex = func_to_obex(f); 19262306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (intf == obex->ctrl_id) { 19562306a36Sopenharmony_ci if (alt != 0) 19662306a36Sopenharmony_ci goto fail; 19762306a36Sopenharmony_ci /* NOP */ 19862306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 19962306a36Sopenharmony_ci "reset obex ttyGS%d control\n", obex->port_num); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci } else if (intf == obex->data_id) { 20262306a36Sopenharmony_ci if (alt > 1) 20362306a36Sopenharmony_ci goto fail; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (obex->port.in->enabled) { 20662306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 20762306a36Sopenharmony_ci "reset obex ttyGS%d\n", obex->port_num); 20862306a36Sopenharmony_ci gserial_disconnect(&obex->port); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (!obex->port.in->desc || !obex->port.out->desc) { 21262306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 21362306a36Sopenharmony_ci "init obex ttyGS%d\n", obex->port_num); 21462306a36Sopenharmony_ci if (config_ep_by_speed(cdev->gadget, f, 21562306a36Sopenharmony_ci obex->port.in) || 21662306a36Sopenharmony_ci config_ep_by_speed(cdev->gadget, f, 21762306a36Sopenharmony_ci obex->port.out)) { 21862306a36Sopenharmony_ci obex->port.out->desc = NULL; 21962306a36Sopenharmony_ci obex->port.in->desc = NULL; 22062306a36Sopenharmony_ci goto fail; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (alt == 1) { 22562306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 22662306a36Sopenharmony_ci "activate obex ttyGS%d\n", obex->port_num); 22762306a36Sopenharmony_ci gserial_connect(&obex->port, obex->port_num); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci } else 23162306a36Sopenharmony_ci goto fail; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci obex->cur_alt = alt; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cifail: 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int obex_get_alt(struct usb_function *f, unsigned intf) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct f_obex *obex = func_to_obex(f); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return obex->cur_alt; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void obex_disable(struct usb_function *f) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct f_obex *obex = func_to_obex(f); 25162306a36Sopenharmony_ci struct usb_composite_dev *cdev = f->config->cdev; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num); 25462306a36Sopenharmony_ci gserial_disconnect(&obex->port); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void obex_connect(struct gserial *g) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct f_obex *obex = port_to_obex(g); 26262306a36Sopenharmony_ci struct usb_composite_dev *cdev = g->func.config->cdev; 26362306a36Sopenharmony_ci int status; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci status = usb_function_activate(&g->func); 26662306a36Sopenharmony_ci if (status) 26762306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 26862306a36Sopenharmony_ci "obex ttyGS%d function activate --> %d\n", 26962306a36Sopenharmony_ci obex->port_num, status); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void obex_disconnect(struct gserial *g) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci struct f_obex *obex = port_to_obex(g); 27562306a36Sopenharmony_ci struct usb_composite_dev *cdev = g->func.config->cdev; 27662306a36Sopenharmony_ci int status; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci status = usb_function_deactivate(&g->func); 27962306a36Sopenharmony_ci if (status) 28062306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, 28162306a36Sopenharmony_ci "obex ttyGS%d function deactivate --> %d\n", 28262306a36Sopenharmony_ci obex->port_num, status); 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci/* Some controllers can't support CDC OBEX ... */ 28862306a36Sopenharmony_cistatic inline bool can_support_obex(struct usb_configuration *c) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci /* Since the first interface is a NOP, we can ignore the 29162306a36Sopenharmony_ci * issue of multi-interface support on most controllers. 29262306a36Sopenharmony_ci * 29362306a36Sopenharmony_ci * Altsettings are mandatory, however... 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (!gadget_is_altset_supported(c->cdev->gadget)) 29662306a36Sopenharmony_ci return false; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* everything else is *probably* fine ... */ 29962306a36Sopenharmony_ci return true; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int obex_bind(struct usb_configuration *c, struct usb_function *f) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct usb_composite_dev *cdev = c->cdev; 30562306a36Sopenharmony_ci struct f_obex *obex = func_to_obex(f); 30662306a36Sopenharmony_ci struct usb_string *us; 30762306a36Sopenharmony_ci int status; 30862306a36Sopenharmony_ci struct usb_ep *ep; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!can_support_obex(c)) 31162306a36Sopenharmony_ci return -EINVAL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci us = usb_gstrings_attach(cdev, obex_strings, 31462306a36Sopenharmony_ci ARRAY_SIZE(obex_string_defs)); 31562306a36Sopenharmony_ci if (IS_ERR(us)) 31662306a36Sopenharmony_ci return PTR_ERR(us); 31762306a36Sopenharmony_ci obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id; 31862306a36Sopenharmony_ci obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id; 31962306a36Sopenharmony_ci obex_data_intf.iInterface = us[OBEX_DATA_IDX].id; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* allocate instance-specific interface IDs, and patch descriptors */ 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci status = usb_interface_id(c, f); 32462306a36Sopenharmony_ci if (status < 0) 32562306a36Sopenharmony_ci goto fail; 32662306a36Sopenharmony_ci obex->ctrl_id = status; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci obex_control_intf.bInterfaceNumber = status; 32962306a36Sopenharmony_ci obex_cdc_union_desc.bMasterInterface0 = status; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci status = usb_interface_id(c, f); 33262306a36Sopenharmony_ci if (status < 0) 33362306a36Sopenharmony_ci goto fail; 33462306a36Sopenharmony_ci obex->data_id = status; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci obex_data_nop_intf.bInterfaceNumber = status; 33762306a36Sopenharmony_ci obex_data_intf.bInterfaceNumber = status; 33862306a36Sopenharmony_ci obex_cdc_union_desc.bSlaveInterface0 = status; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* allocate instance-specific endpoints */ 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci status = -ENODEV; 34362306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc); 34462306a36Sopenharmony_ci if (!ep) 34562306a36Sopenharmony_ci goto fail; 34662306a36Sopenharmony_ci obex->port.in = ep; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc); 34962306a36Sopenharmony_ci if (!ep) 35062306a36Sopenharmony_ci goto fail; 35162306a36Sopenharmony_ci obex->port.out = ep; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* support all relevant hardware speeds... we expect that when 35462306a36Sopenharmony_ci * hardware is dual speed, all bulk-capable endpoints work at 35562306a36Sopenharmony_ci * both speeds 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci obex_hs_ep_in_desc.bEndpointAddress = 35962306a36Sopenharmony_ci obex_fs_ep_in_desc.bEndpointAddress; 36062306a36Sopenharmony_ci obex_hs_ep_out_desc.bEndpointAddress = 36162306a36Sopenharmony_ci obex_fs_ep_out_desc.bEndpointAddress; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci status = usb_assign_descriptors(f, fs_function, hs_function, NULL, 36462306a36Sopenharmony_ci NULL); 36562306a36Sopenharmony_ci if (status) 36662306a36Sopenharmony_ci goto fail; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: IN/%s OUT/%s\n", 36962306a36Sopenharmony_ci obex->port_num, 37062306a36Sopenharmony_ci obex->port.in->name, obex->port.out->name); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cifail: 37562306a36Sopenharmony_ci ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci return status; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci return container_of(to_config_group(item), struct f_serial_opts, 38362306a36Sopenharmony_ci func_inst.group); 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void obex_attr_release(struct config_item *item) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct f_serial_opts *opts = to_f_serial_opts(item); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci usb_put_function_instance(&opts->func_inst); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic struct configfs_item_operations obex_item_ops = { 39462306a36Sopenharmony_ci .release = obex_attr_release, 39562306a36Sopenharmony_ci}; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic ssize_t f_obex_port_num_show(struct config_item *item, char *page) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num); 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciCONFIGFS_ATTR_RO(f_obex_, port_num); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic struct configfs_attribute *acm_attrs[] = { 40562306a36Sopenharmony_ci &f_obex_attr_port_num, 40662306a36Sopenharmony_ci NULL, 40762306a36Sopenharmony_ci}; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic const struct config_item_type obex_func_type = { 41062306a36Sopenharmony_ci .ct_item_ops = &obex_item_ops, 41162306a36Sopenharmony_ci .ct_attrs = acm_attrs, 41262306a36Sopenharmony_ci .ct_owner = THIS_MODULE, 41362306a36Sopenharmony_ci}; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void obex_free_inst(struct usb_function_instance *f) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct f_serial_opts *opts; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci opts = container_of(f, struct f_serial_opts, func_inst); 42062306a36Sopenharmony_ci gserial_free_line(opts->port_num); 42162306a36Sopenharmony_ci kfree(opts); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic struct usb_function_instance *obex_alloc_inst(void) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct f_serial_opts *opts; 42762306a36Sopenharmony_ci int ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci opts = kzalloc(sizeof(*opts), GFP_KERNEL); 43062306a36Sopenharmony_ci if (!opts) 43162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci opts->func_inst.free_func_inst = obex_free_inst; 43462306a36Sopenharmony_ci ret = gserial_alloc_line_no_console(&opts->port_num); 43562306a36Sopenharmony_ci if (ret) { 43662306a36Sopenharmony_ci kfree(opts); 43762306a36Sopenharmony_ci return ERR_PTR(ret); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci config_group_init_type_name(&opts->func_inst.group, "", 44062306a36Sopenharmony_ci &obex_func_type); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return &opts->func_inst; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic void obex_free(struct usb_function *f) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct f_obex *obex; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci obex = func_to_obex(f); 45062306a36Sopenharmony_ci kfree(obex); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void obex_unbind(struct usb_configuration *c, struct usb_function *f) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci usb_free_all_descriptors(f); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic struct usb_function *obex_alloc(struct usb_function_instance *fi) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct f_obex *obex; 46162306a36Sopenharmony_ci struct f_serial_opts *opts; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* allocate and initialize one new instance */ 46462306a36Sopenharmony_ci obex = kzalloc(sizeof(*obex), GFP_KERNEL); 46562306a36Sopenharmony_ci if (!obex) 46662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci opts = container_of(fi, struct f_serial_opts, func_inst); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci obex->port_num = opts->port_num; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci obex->port.connect = obex_connect; 47362306a36Sopenharmony_ci obex->port.disconnect = obex_disconnect; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci obex->port.func.name = "obex"; 47662306a36Sopenharmony_ci /* descriptors are per-instance copies */ 47762306a36Sopenharmony_ci obex->port.func.bind = obex_bind; 47862306a36Sopenharmony_ci obex->port.func.unbind = obex_unbind; 47962306a36Sopenharmony_ci obex->port.func.set_alt = obex_set_alt; 48062306a36Sopenharmony_ci obex->port.func.get_alt = obex_get_alt; 48162306a36Sopenharmony_ci obex->port.func.disable = obex_disable; 48262306a36Sopenharmony_ci obex->port.func.free_func = obex_free; 48362306a36Sopenharmony_ci obex->port.func.bind_deactivated = true; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return &obex->port.func; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc); 48962306a36Sopenharmony_ciMODULE_AUTHOR("Felipe Balbi"); 49062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 491