15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0+
23086775aSFelipe Balbi /*
33086775aSFelipe Balbi * f_obex.c -- USB CDC OBEX function driver
43086775aSFelipe Balbi *
53086775aSFelipe Balbi * Copyright (C) 2008 Nokia Corporation
63086775aSFelipe Balbi * Contact: Felipe Balbi <felipe.balbi@nokia.com>
73086775aSFelipe Balbi *
83086775aSFelipe Balbi * Based on f_acm.c by Al Borchers and David Brownell.
93086775aSFelipe Balbi */
103086775aSFelipe Balbi
113086775aSFelipe Balbi /* #define VERBOSE_DEBUG */
123086775aSFelipe Balbi
135a0e3ad6STejun Heo #include <linux/slab.h>
143086775aSFelipe Balbi #include <linux/kernel.h>
153086775aSFelipe Balbi #include <linux/device.h>
166eb0de82SPaul Gortmaker #include <linux/module.h>
173086775aSFelipe Balbi
183086775aSFelipe Balbi #include "u_serial.h"
193086775aSFelipe Balbi
203086775aSFelipe Balbi
213086775aSFelipe Balbi /*
223086775aSFelipe Balbi * This CDC OBEX function support just packages a TTY-ish byte stream.
233086775aSFelipe Balbi * A user mode server will put it into "raw" mode and handle all the
243086775aSFelipe Balbi * relevant protocol details ... this is just a kernel passthrough.
2521214278SDavid Brownell * When possible, we prevent gadget enumeration until that server is
2621214278SDavid Brownell * ready to handle the commands.
273086775aSFelipe Balbi */
283086775aSFelipe Balbi
293086775aSFelipe Balbi struct f_obex {
303086775aSFelipe Balbi struct gserial port;
313086775aSFelipe Balbi u8 ctrl_id;
323086775aSFelipe Balbi u8 data_id;
333985f3abSFelipe Balbi u8 cur_alt;
343086775aSFelipe Balbi u8 port_num;
353086775aSFelipe Balbi };
363086775aSFelipe Balbi
func_to_obex(struct usb_function * f)373086775aSFelipe Balbi static inline struct f_obex *func_to_obex(struct usb_function *f)
383086775aSFelipe Balbi {
393086775aSFelipe Balbi return container_of(f, struct f_obex, port.func);
403086775aSFelipe Balbi }
413086775aSFelipe Balbi
port_to_obex(struct gserial * p)4221214278SDavid Brownell static inline struct f_obex *port_to_obex(struct gserial *p)
4321214278SDavid Brownell {
4421214278SDavid Brownell return container_of(p, struct f_obex, port);
4521214278SDavid Brownell }
4621214278SDavid Brownell
473086775aSFelipe Balbi /*-------------------------------------------------------------------------*/
483086775aSFelipe Balbi
493086775aSFelipe Balbi #define OBEX_CTRL_IDX 0
503086775aSFelipe Balbi #define OBEX_DATA_IDX 1
513086775aSFelipe Balbi
523086775aSFelipe Balbi static struct usb_string obex_string_defs[] = {
533086775aSFelipe Balbi [OBEX_CTRL_IDX].s = "CDC Object Exchange (OBEX)",
543086775aSFelipe Balbi [OBEX_DATA_IDX].s = "CDC OBEX Data",
553086775aSFelipe Balbi { }, /* end of list */
563086775aSFelipe Balbi };
573086775aSFelipe Balbi
583086775aSFelipe Balbi static struct usb_gadget_strings obex_string_table = {
593086775aSFelipe Balbi .language = 0x0409, /* en-US */
603086775aSFelipe Balbi .strings = obex_string_defs,
613086775aSFelipe Balbi };
623086775aSFelipe Balbi
633086775aSFelipe Balbi static struct usb_gadget_strings *obex_strings[] = {
643086775aSFelipe Balbi &obex_string_table,
653086775aSFelipe Balbi NULL,
663086775aSFelipe Balbi };
673086775aSFelipe Balbi
683086775aSFelipe Balbi /*-------------------------------------------------------------------------*/
693086775aSFelipe Balbi
701d8fc251SAndrzej Pietrasiewicz static struct usb_interface_descriptor obex_control_intf = {
713086775aSFelipe Balbi .bLength = sizeof(obex_control_intf),
723086775aSFelipe Balbi .bDescriptorType = USB_DT_INTERFACE,
733086775aSFelipe Balbi .bInterfaceNumber = 0,
743086775aSFelipe Balbi
753086775aSFelipe Balbi .bAlternateSetting = 0,
763086775aSFelipe Balbi .bNumEndpoints = 0,
773086775aSFelipe Balbi .bInterfaceClass = USB_CLASS_COMM,
783086775aSFelipe Balbi .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX,
793086775aSFelipe Balbi };
803086775aSFelipe Balbi
811d8fc251SAndrzej Pietrasiewicz static struct usb_interface_descriptor obex_data_nop_intf = {
823086775aSFelipe Balbi .bLength = sizeof(obex_data_nop_intf),
833086775aSFelipe Balbi .bDescriptorType = USB_DT_INTERFACE,
843086775aSFelipe Balbi .bInterfaceNumber = 1,
853086775aSFelipe Balbi
863086775aSFelipe Balbi .bAlternateSetting = 0,
873086775aSFelipe Balbi .bNumEndpoints = 0,
883086775aSFelipe Balbi .bInterfaceClass = USB_CLASS_CDC_DATA,
893086775aSFelipe Balbi };
903086775aSFelipe Balbi
911d8fc251SAndrzej Pietrasiewicz static struct usb_interface_descriptor obex_data_intf = {
923086775aSFelipe Balbi .bLength = sizeof(obex_data_intf),
933086775aSFelipe Balbi .bDescriptorType = USB_DT_INTERFACE,
943086775aSFelipe Balbi .bInterfaceNumber = 2,
953086775aSFelipe Balbi
963086775aSFelipe Balbi .bAlternateSetting = 1,
973086775aSFelipe Balbi .bNumEndpoints = 2,
983086775aSFelipe Balbi .bInterfaceClass = USB_CLASS_CDC_DATA,
993086775aSFelipe Balbi };
1003086775aSFelipe Balbi
1011d8fc251SAndrzej Pietrasiewicz static struct usb_cdc_header_desc obex_cdc_header_desc = {
1023086775aSFelipe Balbi .bLength = sizeof(obex_cdc_header_desc),
1033086775aSFelipe Balbi .bDescriptorType = USB_DT_CS_INTERFACE,
1043086775aSFelipe Balbi .bDescriptorSubType = USB_CDC_HEADER_TYPE,
105551509d2SHarvey Harrison .bcdCDC = cpu_to_le16(0x0120),
1063086775aSFelipe Balbi };
1073086775aSFelipe Balbi
1081d8fc251SAndrzej Pietrasiewicz static struct usb_cdc_union_desc obex_cdc_union_desc = {
1093086775aSFelipe Balbi .bLength = sizeof(obex_cdc_union_desc),
1103086775aSFelipe Balbi .bDescriptorType = USB_DT_CS_INTERFACE,
1113086775aSFelipe Balbi .bDescriptorSubType = USB_CDC_UNION_TYPE,
1123086775aSFelipe Balbi .bMasterInterface0 = 1,
1133086775aSFelipe Balbi .bSlaveInterface0 = 2,
1143086775aSFelipe Balbi };
1153086775aSFelipe Balbi
1161d8fc251SAndrzej Pietrasiewicz static struct usb_cdc_obex_desc obex_desc = {
1173086775aSFelipe Balbi .bLength = sizeof(obex_desc),
1183086775aSFelipe Balbi .bDescriptorType = USB_DT_CS_INTERFACE,
1193086775aSFelipe Balbi .bDescriptorSubType = USB_CDC_OBEX_TYPE,
120551509d2SHarvey Harrison .bcdVersion = cpu_to_le16(0x0100),
1213086775aSFelipe Balbi };
1223086775aSFelipe Balbi
1233086775aSFelipe Balbi /* High-Speed Support */
1243086775aSFelipe Balbi
1251d8fc251SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor obex_hs_ep_out_desc = {
1263086775aSFelipe Balbi .bLength = USB_DT_ENDPOINT_SIZE,
1273086775aSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT,
1283086775aSFelipe Balbi
1293086775aSFelipe Balbi .bEndpointAddress = USB_DIR_OUT,
1303086775aSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK,
131551509d2SHarvey Harrison .wMaxPacketSize = cpu_to_le16(512),
1323086775aSFelipe Balbi };
1333086775aSFelipe Balbi
1341d8fc251SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor obex_hs_ep_in_desc = {
1353086775aSFelipe Balbi .bLength = USB_DT_ENDPOINT_SIZE,
1363086775aSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT,
1373086775aSFelipe Balbi
1383086775aSFelipe Balbi .bEndpointAddress = USB_DIR_IN,
1393086775aSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK,
140551509d2SHarvey Harrison .wMaxPacketSize = cpu_to_le16(512),
1413086775aSFelipe Balbi };
1423086775aSFelipe Balbi
1431d8fc251SAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_function[] = {
1443086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_control_intf,
1453086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_cdc_header_desc,
1463086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_desc,
1473086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_cdc_union_desc,
1483086775aSFelipe Balbi
1493086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_data_nop_intf,
1503086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_data_intf,
1513086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_hs_ep_in_desc,
1523086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_hs_ep_out_desc,
1533086775aSFelipe Balbi NULL,
1543086775aSFelipe Balbi };
1553086775aSFelipe Balbi
1563086775aSFelipe Balbi /* Full-Speed Support */
1573086775aSFelipe Balbi
1581d8fc251SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor obex_fs_ep_in_desc = {
1593086775aSFelipe Balbi .bLength = USB_DT_ENDPOINT_SIZE,
1603086775aSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT,
1613086775aSFelipe Balbi
1623086775aSFelipe Balbi .bEndpointAddress = USB_DIR_IN,
1633086775aSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK,
1643086775aSFelipe Balbi };
1653086775aSFelipe Balbi
1661d8fc251SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor obex_fs_ep_out_desc = {
1673086775aSFelipe Balbi .bLength = USB_DT_ENDPOINT_SIZE,
1683086775aSFelipe Balbi .bDescriptorType = USB_DT_ENDPOINT,
1693086775aSFelipe Balbi
1703086775aSFelipe Balbi .bEndpointAddress = USB_DIR_OUT,
1713086775aSFelipe Balbi .bmAttributes = USB_ENDPOINT_XFER_BULK,
1723086775aSFelipe Balbi };
1733086775aSFelipe Balbi
1741d8fc251SAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_function[] = {
1753086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_control_intf,
1763086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_cdc_header_desc,
1773086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_desc,
1783086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_cdc_union_desc,
1793086775aSFelipe Balbi
1803086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_data_nop_intf,
1813086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_data_intf,
1823086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_fs_ep_in_desc,
1833086775aSFelipe Balbi (struct usb_descriptor_header *) &obex_fs_ep_out_desc,
1843086775aSFelipe Balbi NULL,
1853086775aSFelipe Balbi };
1863086775aSFelipe Balbi
1873086775aSFelipe Balbi /*-------------------------------------------------------------------------*/
1883086775aSFelipe Balbi
obex_set_alt(struct usb_function * f,unsigned intf,unsigned alt)1893086775aSFelipe Balbi static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
1903086775aSFelipe Balbi {
1913086775aSFelipe Balbi struct f_obex *obex = func_to_obex(f);
1923086775aSFelipe Balbi struct usb_composite_dev *cdev = f->config->cdev;
1933086775aSFelipe Balbi
1943086775aSFelipe Balbi if (intf == obex->ctrl_id) {
1953086775aSFelipe Balbi if (alt != 0)
1963086775aSFelipe Balbi goto fail;
1973086775aSFelipe Balbi /* NOP */
198b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
199b8b0ea51SRichard Leitner "reset obex ttyGS%d control\n", obex->port_num);
2003086775aSFelipe Balbi
2013086775aSFelipe Balbi } else if (intf == obex->data_id) {
2023086775aSFelipe Balbi if (alt > 1)
2033086775aSFelipe Balbi goto fail;
2043086775aSFelipe Balbi
205d277e1a3SRobert Baldyga if (obex->port.in->enabled) {
206b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
207b8b0ea51SRichard Leitner "reset obex ttyGS%d\n", obex->port_num);
2083086775aSFelipe Balbi gserial_disconnect(&obex->port);
2093086775aSFelipe Balbi }
2103086775aSFelipe Balbi
211ea2a1df7STatyana Brokhman if (!obex->port.in->desc || !obex->port.out->desc) {
212b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
213b8b0ea51SRichard Leitner "init obex ttyGS%d\n", obex->port_num);
214ea2a1df7STatyana Brokhman if (config_ep_by_speed(cdev->gadget, f,
215ea2a1df7STatyana Brokhman obex->port.in) ||
216ea2a1df7STatyana Brokhman config_ep_by_speed(cdev->gadget, f,
217ea2a1df7STatyana Brokhman obex->port.out)) {
218ea2a1df7STatyana Brokhman obex->port.out->desc = NULL;
219ea2a1df7STatyana Brokhman obex->port.in->desc = NULL;
220ea2a1df7STatyana Brokhman goto fail;
221ea2a1df7STatyana Brokhman }
2223086775aSFelipe Balbi }
2233086775aSFelipe Balbi
2243086775aSFelipe Balbi if (alt == 1) {
225b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
226b8b0ea51SRichard Leitner "activate obex ttyGS%d\n", obex->port_num);
2273086775aSFelipe Balbi gserial_connect(&obex->port, obex->port_num);
2283086775aSFelipe Balbi }
2293086775aSFelipe Balbi
2303086775aSFelipe Balbi } else
2313086775aSFelipe Balbi goto fail;
2323086775aSFelipe Balbi
2333985f3abSFelipe Balbi obex->cur_alt = alt;
2343985f3abSFelipe Balbi
2353086775aSFelipe Balbi return 0;
2363086775aSFelipe Balbi
2373086775aSFelipe Balbi fail:
2383086775aSFelipe Balbi return -EINVAL;
2393086775aSFelipe Balbi }
2403086775aSFelipe Balbi
obex_get_alt(struct usb_function * f,unsigned intf)2413086775aSFelipe Balbi static int obex_get_alt(struct usb_function *f, unsigned intf)
2423086775aSFelipe Balbi {
2433086775aSFelipe Balbi struct f_obex *obex = func_to_obex(f);
2443086775aSFelipe Balbi
2453985f3abSFelipe Balbi return obex->cur_alt;
2463086775aSFelipe Balbi }
2473086775aSFelipe Balbi
obex_disable(struct usb_function * f)2483086775aSFelipe Balbi static void obex_disable(struct usb_function *f)
2493086775aSFelipe Balbi {
2503086775aSFelipe Balbi struct f_obex *obex = func_to_obex(f);
2513086775aSFelipe Balbi struct usb_composite_dev *cdev = f->config->cdev;
2523086775aSFelipe Balbi
253b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev, "obex ttyGS%d disable\n", obex->port_num);
2543086775aSFelipe Balbi gserial_disconnect(&obex->port);
2553086775aSFelipe Balbi }
2563086775aSFelipe Balbi
2573086775aSFelipe Balbi /*-------------------------------------------------------------------------*/
2583086775aSFelipe Balbi
obex_connect(struct gserial * g)25921214278SDavid Brownell static void obex_connect(struct gserial *g)
26021214278SDavid Brownell {
26121214278SDavid Brownell struct f_obex *obex = port_to_obex(g);
26221214278SDavid Brownell struct usb_composite_dev *cdev = g->func.config->cdev;
26321214278SDavid Brownell int status;
26421214278SDavid Brownell
26521214278SDavid Brownell status = usb_function_activate(&g->func);
26621214278SDavid Brownell if (status)
267b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
268b8b0ea51SRichard Leitner "obex ttyGS%d function activate --> %d\n",
26921214278SDavid Brownell obex->port_num, status);
27021214278SDavid Brownell }
27121214278SDavid Brownell
obex_disconnect(struct gserial * g)27221214278SDavid Brownell static void obex_disconnect(struct gserial *g)
27321214278SDavid Brownell {
27421214278SDavid Brownell struct f_obex *obex = port_to_obex(g);
27521214278SDavid Brownell struct usb_composite_dev *cdev = g->func.config->cdev;
27621214278SDavid Brownell int status;
27721214278SDavid Brownell
27821214278SDavid Brownell status = usb_function_deactivate(&g->func);
27921214278SDavid Brownell if (status)
280b8b0ea51SRichard Leitner dev_dbg(&cdev->gadget->dev,
281b8b0ea51SRichard Leitner "obex ttyGS%d function deactivate --> %d\n",
28221214278SDavid Brownell obex->port_num, status);
28321214278SDavid Brownell }
28421214278SDavid Brownell
28521214278SDavid Brownell /*-------------------------------------------------------------------------*/
28621214278SDavid Brownell
2871d8fc251SAndrzej Pietrasiewicz /* Some controllers can't support CDC OBEX ... */
can_support_obex(struct usb_configuration * c)2881d8fc251SAndrzej Pietrasiewicz static inline bool can_support_obex(struct usb_configuration *c)
2891d8fc251SAndrzej Pietrasiewicz {
2901d8fc251SAndrzej Pietrasiewicz /* Since the first interface is a NOP, we can ignore the
2911d8fc251SAndrzej Pietrasiewicz * issue of multi-interface support on most controllers.
2921d8fc251SAndrzej Pietrasiewicz *
2931d8fc251SAndrzej Pietrasiewicz * Altsettings are mandatory, however...
2941d8fc251SAndrzej Pietrasiewicz */
295736d093bSRobert Baldyga if (!gadget_is_altset_supported(c->cdev->gadget))
2961d8fc251SAndrzej Pietrasiewicz return false;
2971d8fc251SAndrzej Pietrasiewicz
2981d8fc251SAndrzej Pietrasiewicz /* everything else is *probably* fine ... */
2991d8fc251SAndrzej Pietrasiewicz return true;
3001d8fc251SAndrzej Pietrasiewicz }
3011d8fc251SAndrzej Pietrasiewicz
obex_bind(struct usb_configuration * c,struct usb_function * f)3021d8fc251SAndrzej Pietrasiewicz static int obex_bind(struct usb_configuration *c, struct usb_function *f)
3033086775aSFelipe Balbi {
3043086775aSFelipe Balbi struct usb_composite_dev *cdev = c->cdev;
3053086775aSFelipe Balbi struct f_obex *obex = func_to_obex(f);
3061af877c4SAndrzej Pietrasiewicz struct usb_string *us;
3073086775aSFelipe Balbi int status;
3083086775aSFelipe Balbi struct usb_ep *ep;
3093086775aSFelipe Balbi
3101d8fc251SAndrzej Pietrasiewicz if (!can_support_obex(c))
3111d8fc251SAndrzej Pietrasiewicz return -EINVAL;
3121d8fc251SAndrzej Pietrasiewicz
3131af877c4SAndrzej Pietrasiewicz us = usb_gstrings_attach(cdev, obex_strings,
3141af877c4SAndrzej Pietrasiewicz ARRAY_SIZE(obex_string_defs));
3151af877c4SAndrzej Pietrasiewicz if (IS_ERR(us))
3161af877c4SAndrzej Pietrasiewicz return PTR_ERR(us);
3171af877c4SAndrzej Pietrasiewicz obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id;
3181af877c4SAndrzej Pietrasiewicz obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id;
3191af877c4SAndrzej Pietrasiewicz obex_data_intf.iInterface = us[OBEX_DATA_IDX].id;
3201d8fc251SAndrzej Pietrasiewicz
3213086775aSFelipe Balbi /* allocate instance-specific interface IDs, and patch descriptors */
3223086775aSFelipe Balbi
3233086775aSFelipe Balbi status = usb_interface_id(c, f);
3243086775aSFelipe Balbi if (status < 0)
3253086775aSFelipe Balbi goto fail;
3263086775aSFelipe Balbi obex->ctrl_id = status;
3273086775aSFelipe Balbi
3283086775aSFelipe Balbi obex_control_intf.bInterfaceNumber = status;
3293086775aSFelipe Balbi obex_cdc_union_desc.bMasterInterface0 = status;
3303086775aSFelipe Balbi
3313086775aSFelipe Balbi status = usb_interface_id(c, f);
3323086775aSFelipe Balbi if (status < 0)
3333086775aSFelipe Balbi goto fail;
3343086775aSFelipe Balbi obex->data_id = status;
3353086775aSFelipe Balbi
3363086775aSFelipe Balbi obex_data_nop_intf.bInterfaceNumber = status;
3373086775aSFelipe Balbi obex_data_intf.bInterfaceNumber = status;
3383086775aSFelipe Balbi obex_cdc_union_desc.bSlaveInterface0 = status;
3393086775aSFelipe Balbi
3403086775aSFelipe Balbi /* allocate instance-specific endpoints */
3413086775aSFelipe Balbi
3426ee3e8e6SWei Yongjun status = -ENODEV;
3433086775aSFelipe Balbi ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
3443086775aSFelipe Balbi if (!ep)
3453086775aSFelipe Balbi goto fail;
3463086775aSFelipe Balbi obex->port.in = ep;
3473086775aSFelipe Balbi
3483086775aSFelipe Balbi ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc);
3493086775aSFelipe Balbi if (!ep)
3503086775aSFelipe Balbi goto fail;
3513086775aSFelipe Balbi obex->port.out = ep;
3523086775aSFelipe Balbi
3533086775aSFelipe Balbi /* support all relevant hardware speeds... we expect that when
3543086775aSFelipe Balbi * hardware is dual speed, all bulk-capable endpoints work at
3553086775aSFelipe Balbi * both speeds
3563086775aSFelipe Balbi */
3573086775aSFelipe Balbi
3583086775aSFelipe Balbi obex_hs_ep_in_desc.bEndpointAddress =
3593086775aSFelipe Balbi obex_fs_ep_in_desc.bEndpointAddress;
3603086775aSFelipe Balbi obex_hs_ep_out_desc.bEndpointAddress =
3613086775aSFelipe Balbi obex_fs_ep_out_desc.bEndpointAddress;
3623086775aSFelipe Balbi
363eaef50c7SJohn Youn status = usb_assign_descriptors(f, fs_function, hs_function, NULL,
364eaef50c7SJohn Youn NULL);
36510287baeSSebastian Andrzej Siewior if (status)
36610287baeSSebastian Andrzej Siewior goto fail;
3673086775aSFelipe Balbi
368333ab99eSLinyu Yuan dev_dbg(&cdev->gadget->dev, "obex ttyGS%d: IN/%s OUT/%s\n",
3693086775aSFelipe Balbi obex->port_num,
3703086775aSFelipe Balbi obex->port.in->name, obex->port.out->name);
3713086775aSFelipe Balbi
3723086775aSFelipe Balbi return 0;
3733086775aSFelipe Balbi
3743086775aSFelipe Balbi fail:
3753086775aSFelipe Balbi ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
3763086775aSFelipe Balbi
3773086775aSFelipe Balbi return status;
3783086775aSFelipe Balbi }
3793086775aSFelipe Balbi
to_f_serial_opts(struct config_item * item)380ecfd3f7bSAndrzej Pietrasiewicz static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
381ecfd3f7bSAndrzej Pietrasiewicz {
382ecfd3f7bSAndrzej Pietrasiewicz return container_of(to_config_group(item), struct f_serial_opts,
383ecfd3f7bSAndrzej Pietrasiewicz func_inst.group);
384ecfd3f7bSAndrzej Pietrasiewicz }
385ecfd3f7bSAndrzej Pietrasiewicz
obex_attr_release(struct config_item * item)386ecfd3f7bSAndrzej Pietrasiewicz static void obex_attr_release(struct config_item *item)
387ecfd3f7bSAndrzej Pietrasiewicz {
388ecfd3f7bSAndrzej Pietrasiewicz struct f_serial_opts *opts = to_f_serial_opts(item);
389ecfd3f7bSAndrzej Pietrasiewicz
390ecfd3f7bSAndrzej Pietrasiewicz usb_put_function_instance(&opts->func_inst);
391ecfd3f7bSAndrzej Pietrasiewicz }
392ecfd3f7bSAndrzej Pietrasiewicz
393e715bc42SChristophe JAILLET static const struct configfs_item_operations obex_item_ops = {
394ecfd3f7bSAndrzej Pietrasiewicz .release = obex_attr_release,
395ecfd3f7bSAndrzej Pietrasiewicz };
396ecfd3f7bSAndrzej Pietrasiewicz
f_obex_port_num_show(struct config_item * item,char * page)3973da5e4c1SChristoph Hellwig static ssize_t f_obex_port_num_show(struct config_item *item, char *page)
398ecfd3f7bSAndrzej Pietrasiewicz {
3993da5e4c1SChristoph Hellwig return sprintf(page, "%u\n", to_f_serial_opts(item)->port_num);
400ecfd3f7bSAndrzej Pietrasiewicz }
401ecfd3f7bSAndrzej Pietrasiewicz
4023da5e4c1SChristoph Hellwig CONFIGFS_ATTR_RO(f_obex_, port_num);
403ecfd3f7bSAndrzej Pietrasiewicz
404ecfd3f7bSAndrzej Pietrasiewicz static struct configfs_attribute *acm_attrs[] = {
4053da5e4c1SChristoph Hellwig &f_obex_attr_port_num,
406ecfd3f7bSAndrzej Pietrasiewicz NULL,
407ecfd3f7bSAndrzej Pietrasiewicz };
408ecfd3f7bSAndrzej Pietrasiewicz
40997363902SBhumika Goyal static const struct config_item_type obex_func_type = {
410ecfd3f7bSAndrzej Pietrasiewicz .ct_item_ops = &obex_item_ops,
411ecfd3f7bSAndrzej Pietrasiewicz .ct_attrs = acm_attrs,
412ecfd3f7bSAndrzej Pietrasiewicz .ct_owner = THIS_MODULE,
413ecfd3f7bSAndrzej Pietrasiewicz };
414ecfd3f7bSAndrzej Pietrasiewicz
obex_free_inst(struct usb_function_instance * f)4151d8fc251SAndrzej Pietrasiewicz static void obex_free_inst(struct usb_function_instance *f)
4161d8fc251SAndrzej Pietrasiewicz {
4171d8fc251SAndrzej Pietrasiewicz struct f_serial_opts *opts;
4181d8fc251SAndrzej Pietrasiewicz
4191d8fc251SAndrzej Pietrasiewicz opts = container_of(f, struct f_serial_opts, func_inst);
4201d8fc251SAndrzej Pietrasiewicz gserial_free_line(opts->port_num);
4211d8fc251SAndrzej Pietrasiewicz kfree(opts);
4221d8fc251SAndrzej Pietrasiewicz }
4231d8fc251SAndrzej Pietrasiewicz
obex_alloc_inst(void)4241d8fc251SAndrzej Pietrasiewicz static struct usb_function_instance *obex_alloc_inst(void)
4251d8fc251SAndrzej Pietrasiewicz {
4261d8fc251SAndrzej Pietrasiewicz struct f_serial_opts *opts;
4271d8fc251SAndrzej Pietrasiewicz int ret;
4281d8fc251SAndrzej Pietrasiewicz
429*bf4afc53SLinus Torvalds opts = kzalloc_obj(*opts);
4301d8fc251SAndrzej Pietrasiewicz if (!opts)
4311d8fc251SAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
4321d8fc251SAndrzej Pietrasiewicz
4331d8fc251SAndrzej Pietrasiewicz opts->func_inst.free_func_inst = obex_free_inst;
434b417343cSMichał Mirosław ret = gserial_alloc_line_no_console(&opts->port_num);
4351d8fc251SAndrzej Pietrasiewicz if (ret) {
4361d8fc251SAndrzej Pietrasiewicz kfree(opts);
4371d8fc251SAndrzej Pietrasiewicz return ERR_PTR(ret);
4381d8fc251SAndrzej Pietrasiewicz }
439ecfd3f7bSAndrzej Pietrasiewicz config_group_init_type_name(&opts->func_inst.group, "",
440ecfd3f7bSAndrzej Pietrasiewicz &obex_func_type);
4411d8fc251SAndrzej Pietrasiewicz
4421d8fc251SAndrzej Pietrasiewicz return &opts->func_inst;
4431d8fc251SAndrzej Pietrasiewicz }
4441d8fc251SAndrzej Pietrasiewicz
obex_free(struct usb_function * f)4451d8fc251SAndrzej Pietrasiewicz static void obex_free(struct usb_function *f)
4461d8fc251SAndrzej Pietrasiewicz {
4471d8fc251SAndrzej Pietrasiewicz struct f_obex *obex;
4481d8fc251SAndrzej Pietrasiewicz
4491d8fc251SAndrzej Pietrasiewicz obex = func_to_obex(f);
4501d8fc251SAndrzej Pietrasiewicz kfree(obex);
4511d8fc251SAndrzej Pietrasiewicz }
4521d8fc251SAndrzej Pietrasiewicz
obex_unbind(struct usb_configuration * c,struct usb_function * f)4531d8fc251SAndrzej Pietrasiewicz static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
4541d8fc251SAndrzej Pietrasiewicz {
4551d8fc251SAndrzej Pietrasiewicz usb_free_all_descriptors(f);
4561d8fc251SAndrzej Pietrasiewicz }
4571d8fc251SAndrzej Pietrasiewicz
obex_alloc(struct usb_function_instance * fi)4589272fe5aSJingoo Han static struct usb_function *obex_alloc(struct usb_function_instance *fi)
4591d8fc251SAndrzej Pietrasiewicz {
4601d8fc251SAndrzej Pietrasiewicz struct f_obex *obex;
4611d8fc251SAndrzej Pietrasiewicz struct f_serial_opts *opts;
4621d8fc251SAndrzej Pietrasiewicz
4631d8fc251SAndrzej Pietrasiewicz /* allocate and initialize one new instance */
464*bf4afc53SLinus Torvalds obex = kzalloc_obj(*obex);
4651d8fc251SAndrzej Pietrasiewicz if (!obex)
4661d8fc251SAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
4671d8fc251SAndrzej Pietrasiewicz
4681d8fc251SAndrzej Pietrasiewicz opts = container_of(fi, struct f_serial_opts, func_inst);
4691d8fc251SAndrzej Pietrasiewicz
4701d8fc251SAndrzej Pietrasiewicz obex->port_num = opts->port_num;
4711d8fc251SAndrzej Pietrasiewicz
4721d8fc251SAndrzej Pietrasiewicz obex->port.connect = obex_connect;
4731d8fc251SAndrzej Pietrasiewicz obex->port.disconnect = obex_disconnect;
4741d8fc251SAndrzej Pietrasiewicz
4751d8fc251SAndrzej Pietrasiewicz obex->port.func.name = "obex";
4761d8fc251SAndrzej Pietrasiewicz /* descriptors are per-instance copies */
4771d8fc251SAndrzej Pietrasiewicz obex->port.func.bind = obex_bind;
4781d8fc251SAndrzej Pietrasiewicz obex->port.func.unbind = obex_unbind;
4791d8fc251SAndrzej Pietrasiewicz obex->port.func.set_alt = obex_set_alt;
4801d8fc251SAndrzej Pietrasiewicz obex->port.func.get_alt = obex_get_alt;
4811d8fc251SAndrzej Pietrasiewicz obex->port.func.disable = obex_disable;
4821d8fc251SAndrzej Pietrasiewicz obex->port.func.free_func = obex_free;
4834cfbd952SRobert Baldyga obex->port.func.bind_deactivated = true;
4841d8fc251SAndrzej Pietrasiewicz
4851d8fc251SAndrzej Pietrasiewicz return &obex->port.func;
4861d8fc251SAndrzej Pietrasiewicz }
4871d8fc251SAndrzej Pietrasiewicz
4881d8fc251SAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
4893086775aSFelipe Balbi MODULE_AUTHOR("Felipe Balbi");
4901cb9ba5eSJeff Johnson MODULE_DESCRIPTION("USB CDC OBEX function driver");
4913086775aSFelipe Balbi MODULE_LICENSE("GPL");
492