162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * f_phonet.c -- USB CDC Phonet function
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Rémi Denis-Courmont
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/mm.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/device.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/netdevice.h>
1762306a36Sopenharmony_ci#include <linux/if_ether.h>
1862306a36Sopenharmony_ci#include <linux/if_phonet.h>
1962306a36Sopenharmony_ci#include <linux/if_arp.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/usb/ch9.h>
2262306a36Sopenharmony_ci#include <linux/usb/cdc.h>
2362306a36Sopenharmony_ci#include <linux/usb/composite.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "u_phonet.h"
2662306a36Sopenharmony_ci#include "u_ether.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define PN_MEDIA_USB	0x1B
2962306a36Sopenharmony_ci#define MAXPACKET	512
3062306a36Sopenharmony_ci#if (PAGE_SIZE % MAXPACKET)
3162306a36Sopenharmony_ci#error MAXPACKET must divide PAGE_SIZE!
3262306a36Sopenharmony_ci#endif
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct phonet_port {
3762306a36Sopenharmony_ci	struct f_phonet			*usb;
3862306a36Sopenharmony_ci	spinlock_t			lock;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct f_phonet {
4262306a36Sopenharmony_ci	struct usb_function		function;
4362306a36Sopenharmony_ci	struct {
4462306a36Sopenharmony_ci		struct sk_buff		*skb;
4562306a36Sopenharmony_ci		spinlock_t		lock;
4662306a36Sopenharmony_ci	} rx;
4762306a36Sopenharmony_ci	struct net_device		*dev;
4862306a36Sopenharmony_ci	struct usb_ep			*in_ep, *out_ep;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	struct usb_request		*in_req;
5162306a36Sopenharmony_ci	struct usb_request		*out_reqv[];
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int phonet_rxq_size = 17;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic inline struct f_phonet *func_to_pn(struct usb_function *f)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	return container_of(f, struct f_phonet, function);
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define USB_CDC_SUBCLASS_PHONET	0xfe
6462306a36Sopenharmony_ci#define USB_CDC_PHONET_TYPE	0xab
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic struct usb_interface_descriptor
6762306a36Sopenharmony_cipn_control_intf_desc = {
6862306a36Sopenharmony_ci	.bLength =		sizeof pn_control_intf_desc,
6962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* .bInterfaceNumber =	DYNAMIC, */
7262306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_COMM,
7362306a36Sopenharmony_ci	.bInterfaceSubClass =	USB_CDC_SUBCLASS_PHONET,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic const struct usb_cdc_header_desc
7762306a36Sopenharmony_cipn_header_desc = {
7862306a36Sopenharmony_ci	.bLength =		sizeof pn_header_desc,
7962306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
8062306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
8162306a36Sopenharmony_ci	.bcdCDC =		cpu_to_le16(0x0110),
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic const struct usb_cdc_header_desc
8562306a36Sopenharmony_cipn_phonet_desc = {
8662306a36Sopenharmony_ci	.bLength =		sizeof pn_phonet_desc,
8762306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
8862306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_PHONET_TYPE,
8962306a36Sopenharmony_ci	.bcdCDC =		cpu_to_le16(0x1505), /* ??? */
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic struct usb_cdc_union_desc
9362306a36Sopenharmony_cipn_union_desc = {
9462306a36Sopenharmony_ci	.bLength =		sizeof pn_union_desc,
9562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_CS_INTERFACE,
9662306a36Sopenharmony_ci	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* .bMasterInterface0 =	DYNAMIC, */
9962306a36Sopenharmony_ci	/* .bSlaveInterface0 =	DYNAMIC, */
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic struct usb_interface_descriptor
10362306a36Sopenharmony_cipn_data_nop_intf_desc = {
10462306a36Sopenharmony_ci	.bLength =		sizeof pn_data_nop_intf_desc,
10562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* .bInterfaceNumber =	DYNAMIC, */
10862306a36Sopenharmony_ci	.bAlternateSetting =	0,
10962306a36Sopenharmony_ci	.bNumEndpoints =	0,
11062306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_CDC_DATA,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic struct usb_interface_descriptor
11462306a36Sopenharmony_cipn_data_intf_desc = {
11562306a36Sopenharmony_ci	.bLength =		sizeof pn_data_intf_desc,
11662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_INTERFACE,
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* .bInterfaceNumber =	DYNAMIC, */
11962306a36Sopenharmony_ci	.bAlternateSetting =	1,
12062306a36Sopenharmony_ci	.bNumEndpoints =	2,
12162306a36Sopenharmony_ci	.bInterfaceClass =	USB_CLASS_CDC_DATA,
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct usb_endpoint_descriptor
12562306a36Sopenharmony_cipn_fs_sink_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};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor
13462306a36Sopenharmony_cipn_hs_sink_desc = {
13562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
13662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_OUT,
13962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
14062306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(MAXPACKET),
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic struct usb_endpoint_descriptor
14462306a36Sopenharmony_cipn_fs_source_desc = {
14562306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
14662306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
14962306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
15062306a36Sopenharmony_ci};
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic struct usb_endpoint_descriptor
15362306a36Sopenharmony_cipn_hs_source_desc = {
15462306a36Sopenharmony_ci	.bLength =		USB_DT_ENDPOINT_SIZE,
15562306a36Sopenharmony_ci	.bDescriptorType =	USB_DT_ENDPOINT,
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	.bEndpointAddress =	USB_DIR_IN,
15862306a36Sopenharmony_ci	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
15962306a36Sopenharmony_ci	.wMaxPacketSize =	cpu_to_le16(512),
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic struct usb_descriptor_header *fs_pn_function[] = {
16362306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_control_intf_desc,
16462306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_header_desc,
16562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_phonet_desc,
16662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_union_desc,
16762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_data_nop_intf_desc,
16862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_data_intf_desc,
16962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_fs_sink_desc,
17062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_fs_source_desc,
17162306a36Sopenharmony_ci	NULL,
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic struct usb_descriptor_header *hs_pn_function[] = {
17562306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_control_intf_desc,
17662306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_header_desc,
17762306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_phonet_desc,
17862306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_union_desc,
17962306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_data_nop_intf_desc,
18062306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_data_intf_desc,
18162306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_hs_sink_desc,
18262306a36Sopenharmony_ci	(struct usb_descriptor_header *) &pn_hs_source_desc,
18362306a36Sopenharmony_ci	NULL,
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic int pn_net_open(struct net_device *dev)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	netif_wake_queue(dev);
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int pn_net_close(struct net_device *dev)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	netif_stop_queue(dev);
19762306a36Sopenharmony_ci	return 0;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void pn_tx_complete(struct usb_ep *ep, struct usb_request *req)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct f_phonet *fp = ep->driver_data;
20362306a36Sopenharmony_ci	struct net_device *dev = fp->dev;
20462306a36Sopenharmony_ci	struct sk_buff *skb = req->context;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	switch (req->status) {
20762306a36Sopenharmony_ci	case 0:
20862306a36Sopenharmony_ci		dev->stats.tx_packets++;
20962306a36Sopenharmony_ci		dev->stats.tx_bytes += skb->len;
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	case -ESHUTDOWN: /* disconnected */
21362306a36Sopenharmony_ci	case -ECONNRESET: /* disabled */
21462306a36Sopenharmony_ci		dev->stats.tx_aborted_errors++;
21562306a36Sopenharmony_ci		fallthrough;
21662306a36Sopenharmony_ci	default:
21762306a36Sopenharmony_ci		dev->stats.tx_errors++;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	dev_kfree_skb_any(skb);
22162306a36Sopenharmony_ci	netif_wake_queue(dev);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic netdev_tx_t pn_net_xmit(struct sk_buff *skb, struct net_device *dev)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct phonet_port *port = netdev_priv(dev);
22762306a36Sopenharmony_ci	struct f_phonet *fp;
22862306a36Sopenharmony_ci	struct usb_request *req;
22962306a36Sopenharmony_ci	unsigned long flags;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (skb->protocol != htons(ETH_P_PHONET))
23262306a36Sopenharmony_ci		goto out;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
23562306a36Sopenharmony_ci	fp = port->usb;
23662306a36Sopenharmony_ci	if (unlikely(!fp)) /* race with carrier loss */
23762306a36Sopenharmony_ci		goto out_unlock;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	req = fp->in_req;
24062306a36Sopenharmony_ci	req->buf = skb->data;
24162306a36Sopenharmony_ci	req->length = skb->len;
24262306a36Sopenharmony_ci	req->complete = pn_tx_complete;
24362306a36Sopenharmony_ci	req->zero = 1;
24462306a36Sopenharmony_ci	req->context = skb;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC)))
24762306a36Sopenharmony_ci		goto out_unlock;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	netif_stop_queue(dev);
25062306a36Sopenharmony_ci	skb = NULL;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ciout_unlock:
25362306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
25462306a36Sopenharmony_ciout:
25562306a36Sopenharmony_ci	if (unlikely(skb)) {
25662306a36Sopenharmony_ci		dev_kfree_skb(skb);
25762306a36Sopenharmony_ci		dev->stats.tx_dropped++;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	return NETDEV_TX_OK;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic const struct net_device_ops pn_netdev_ops = {
26362306a36Sopenharmony_ci	.ndo_open	= pn_net_open,
26462306a36Sopenharmony_ci	.ndo_stop	= pn_net_close,
26562306a36Sopenharmony_ci	.ndo_start_xmit	= pn_net_xmit,
26662306a36Sopenharmony_ci};
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_cistatic void pn_net_setup(struct net_device *dev)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	const u8 addr = PN_MEDIA_USB;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	dev->features		= 0;
27362306a36Sopenharmony_ci	dev->type		= ARPHRD_PHONET;
27462306a36Sopenharmony_ci	dev->flags		= IFF_POINTOPOINT | IFF_NOARP;
27562306a36Sopenharmony_ci	dev->mtu		= PHONET_DEV_MTU;
27662306a36Sopenharmony_ci	dev->min_mtu		= PHONET_MIN_MTU;
27762306a36Sopenharmony_ci	dev->max_mtu		= PHONET_MAX_MTU;
27862306a36Sopenharmony_ci	dev->hard_header_len	= 1;
27962306a36Sopenharmony_ci	dev->addr_len		= 1;
28062306a36Sopenharmony_ci	dev_addr_set(dev, &addr);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dev->tx_queue_len	= 1;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	dev->netdev_ops		= &pn_netdev_ops;
28562306a36Sopenharmony_ci	dev->needs_free_netdev	= true;
28662306a36Sopenharmony_ci	dev->header_ops		= &phonet_header_ops;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/*
29262306a36Sopenharmony_ci * Queue buffer for data from the host
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_cistatic int
29562306a36Sopenharmony_cipn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	struct page *page;
29862306a36Sopenharmony_ci	int err;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC);
30162306a36Sopenharmony_ci	if (!page)
30262306a36Sopenharmony_ci		return -ENOMEM;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	req->buf = page_address(page);
30562306a36Sopenharmony_ci	req->length = PAGE_SIZE;
30662306a36Sopenharmony_ci	req->context = page;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	err = usb_ep_queue(fp->out_ep, req, gfp_flags);
30962306a36Sopenharmony_ci	if (unlikely(err))
31062306a36Sopenharmony_ci		put_page(page);
31162306a36Sopenharmony_ci	return err;
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
31562306a36Sopenharmony_ci{
31662306a36Sopenharmony_ci	struct f_phonet *fp = ep->driver_data;
31762306a36Sopenharmony_ci	struct net_device *dev = fp->dev;
31862306a36Sopenharmony_ci	struct page *page = req->context;
31962306a36Sopenharmony_ci	struct sk_buff *skb;
32062306a36Sopenharmony_ci	unsigned long flags;
32162306a36Sopenharmony_ci	int status = req->status;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	switch (status) {
32462306a36Sopenharmony_ci	case 0:
32562306a36Sopenharmony_ci		spin_lock_irqsave(&fp->rx.lock, flags);
32662306a36Sopenharmony_ci		skb = fp->rx.skb;
32762306a36Sopenharmony_ci		if (!skb)
32862306a36Sopenharmony_ci			skb = fp->rx.skb = netdev_alloc_skb(dev, 12);
32962306a36Sopenharmony_ci		if (req->actual < req->length) /* Last fragment */
33062306a36Sopenharmony_ci			fp->rx.skb = NULL;
33162306a36Sopenharmony_ci		spin_unlock_irqrestore(&fp->rx.lock, flags);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		if (unlikely(!skb))
33462306a36Sopenharmony_ci			break;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if (skb->len == 0) { /* First fragment */
33762306a36Sopenharmony_ci			skb->protocol = htons(ETH_P_PHONET);
33862306a36Sopenharmony_ci			skb_reset_mac_header(skb);
33962306a36Sopenharmony_ci			/* Can't use pskb_pull() on page in IRQ */
34062306a36Sopenharmony_ci			skb_put_data(skb, page_address(page), 1);
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
34462306a36Sopenharmony_ci				skb->len <= 1, req->actual, PAGE_SIZE);
34562306a36Sopenharmony_ci		page = NULL;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		if (req->actual < req->length) { /* Last fragment */
34862306a36Sopenharmony_ci			skb->dev = dev;
34962306a36Sopenharmony_ci			dev->stats.rx_packets++;
35062306a36Sopenharmony_ci			dev->stats.rx_bytes += skb->len;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci			netif_rx(skb);
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci		break;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	/* Do not resubmit in these cases: */
35762306a36Sopenharmony_ci	case -ESHUTDOWN: /* disconnect */
35862306a36Sopenharmony_ci	case -ECONNABORTED: /* hw reset */
35962306a36Sopenharmony_ci	case -ECONNRESET: /* dequeued (unlink or netif down) */
36062306a36Sopenharmony_ci		req = NULL;
36162306a36Sopenharmony_ci		break;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	/* Do resubmit in these cases: */
36462306a36Sopenharmony_ci	case -EOVERFLOW: /* request buffer overflow */
36562306a36Sopenharmony_ci		dev->stats.rx_over_errors++;
36662306a36Sopenharmony_ci		fallthrough;
36762306a36Sopenharmony_ci	default:
36862306a36Sopenharmony_ci		dev->stats.rx_errors++;
36962306a36Sopenharmony_ci		break;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (page)
37362306a36Sopenharmony_ci		put_page(page);
37462306a36Sopenharmony_ci	if (req)
37562306a36Sopenharmony_ci		pn_rx_submit(fp, req, GFP_ATOMIC);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void __pn_reset(struct usb_function *f)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct f_phonet *fp = func_to_pn(f);
38362306a36Sopenharmony_ci	struct net_device *dev = fp->dev;
38462306a36Sopenharmony_ci	struct phonet_port *port = netdev_priv(dev);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	netif_carrier_off(dev);
38762306a36Sopenharmony_ci	port->usb = NULL;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	usb_ep_disable(fp->out_ep);
39062306a36Sopenharmony_ci	usb_ep_disable(fp->in_ep);
39162306a36Sopenharmony_ci	if (fp->rx.skb) {
39262306a36Sopenharmony_ci		dev_kfree_skb_irq(fp->rx.skb);
39362306a36Sopenharmony_ci		fp->rx.skb = NULL;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct f_phonet *fp = func_to_pn(f);
40062306a36Sopenharmony_ci	struct usb_gadget *gadget = fp->function.config->cdev->gadget;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (intf == pn_control_intf_desc.bInterfaceNumber)
40362306a36Sopenharmony_ci		/* control interface, no altsetting */
40462306a36Sopenharmony_ci		return (alt > 0) ? -EINVAL : 0;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (intf == pn_data_intf_desc.bInterfaceNumber) {
40762306a36Sopenharmony_ci		struct net_device *dev = fp->dev;
40862306a36Sopenharmony_ci		struct phonet_port *port = netdev_priv(dev);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci		/* data intf (0: inactive, 1: active) */
41162306a36Sopenharmony_ci		if (alt > 1)
41262306a36Sopenharmony_ci			return -EINVAL;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci		spin_lock(&port->lock);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		if (fp->in_ep->enabled)
41762306a36Sopenharmony_ci			__pn_reset(f);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		if (alt == 1) {
42062306a36Sopenharmony_ci			int i;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci			if (config_ep_by_speed(gadget, f, fp->in_ep) ||
42362306a36Sopenharmony_ci			    config_ep_by_speed(gadget, f, fp->out_ep)) {
42462306a36Sopenharmony_ci				fp->in_ep->desc = NULL;
42562306a36Sopenharmony_ci				fp->out_ep->desc = NULL;
42662306a36Sopenharmony_ci				spin_unlock(&port->lock);
42762306a36Sopenharmony_ci				return -EINVAL;
42862306a36Sopenharmony_ci			}
42962306a36Sopenharmony_ci			usb_ep_enable(fp->out_ep);
43062306a36Sopenharmony_ci			usb_ep_enable(fp->in_ep);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci			port->usb = fp;
43362306a36Sopenharmony_ci			fp->out_ep->driver_data = fp;
43462306a36Sopenharmony_ci			fp->in_ep->driver_data = fp;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci			netif_carrier_on(dev);
43762306a36Sopenharmony_ci			for (i = 0; i < phonet_rxq_size; i++)
43862306a36Sopenharmony_ci				pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC);
43962306a36Sopenharmony_ci		}
44062306a36Sopenharmony_ci		spin_unlock(&port->lock);
44162306a36Sopenharmony_ci		return 0;
44262306a36Sopenharmony_ci	}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return -EINVAL;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int pn_get_alt(struct usb_function *f, unsigned intf)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct f_phonet *fp = func_to_pn(f);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (intf == pn_control_intf_desc.bInterfaceNumber)
45262306a36Sopenharmony_ci		return 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (intf == pn_data_intf_desc.bInterfaceNumber) {
45562306a36Sopenharmony_ci		struct phonet_port *port = netdev_priv(fp->dev);
45662306a36Sopenharmony_ci		u8 alt;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		spin_lock(&port->lock);
45962306a36Sopenharmony_ci		alt = port->usb != NULL;
46062306a36Sopenharmony_ci		spin_unlock(&port->lock);
46162306a36Sopenharmony_ci		return alt;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	return -EINVAL;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic void pn_disconnect(struct usb_function *f)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct f_phonet *fp = func_to_pn(f);
47062306a36Sopenharmony_ci	struct phonet_port *port = netdev_priv(fp->dev);
47162306a36Sopenharmony_ci	unsigned long flags;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	/* remain disabled until set_alt */
47462306a36Sopenharmony_ci	spin_lock_irqsave(&port->lock, flags);
47562306a36Sopenharmony_ci	__pn_reset(f);
47662306a36Sopenharmony_ci	spin_unlock_irqrestore(&port->lock, flags);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic int pn_bind(struct usb_configuration *c, struct usb_function *f)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct usb_composite_dev *cdev = c->cdev;
48462306a36Sopenharmony_ci	struct usb_gadget *gadget = cdev->gadget;
48562306a36Sopenharmony_ci	struct f_phonet *fp = func_to_pn(f);
48662306a36Sopenharmony_ci	struct usb_ep *ep;
48762306a36Sopenharmony_ci	int status, i;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	struct f_phonet_opts *phonet_opts;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	/*
49462306a36Sopenharmony_ci	 * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
49562306a36Sopenharmony_ci	 * configurations are bound in sequence with list_for_each_entry,
49662306a36Sopenharmony_ci	 * in each configuration its functions are bound in sequence
49762306a36Sopenharmony_ci	 * with list_for_each_entry, so we assume no race condition
49862306a36Sopenharmony_ci	 * with regard to phonet_opts->bound access
49962306a36Sopenharmony_ci	 */
50062306a36Sopenharmony_ci	if (!phonet_opts->bound) {
50162306a36Sopenharmony_ci		gphonet_set_gadget(phonet_opts->net, gadget);
50262306a36Sopenharmony_ci		status = gphonet_register_netdev(phonet_opts->net);
50362306a36Sopenharmony_ci		if (status)
50462306a36Sopenharmony_ci			return status;
50562306a36Sopenharmony_ci		phonet_opts->bound = true;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* Reserve interface IDs */
50962306a36Sopenharmony_ci	status = usb_interface_id(c, f);
51062306a36Sopenharmony_ci	if (status < 0)
51162306a36Sopenharmony_ci		goto err;
51262306a36Sopenharmony_ci	pn_control_intf_desc.bInterfaceNumber = status;
51362306a36Sopenharmony_ci	pn_union_desc.bMasterInterface0 = status;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	status = usb_interface_id(c, f);
51662306a36Sopenharmony_ci	if (status < 0)
51762306a36Sopenharmony_ci		goto err;
51862306a36Sopenharmony_ci	pn_data_nop_intf_desc.bInterfaceNumber = status;
51962306a36Sopenharmony_ci	pn_data_intf_desc.bInterfaceNumber = status;
52062306a36Sopenharmony_ci	pn_union_desc.bSlaveInterface0 = status;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	/* Reserve endpoints */
52362306a36Sopenharmony_ci	status = -ENODEV;
52462306a36Sopenharmony_ci	ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc);
52562306a36Sopenharmony_ci	if (!ep)
52662306a36Sopenharmony_ci		goto err;
52762306a36Sopenharmony_ci	fp->out_ep = ep;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc);
53062306a36Sopenharmony_ci	if (!ep)
53162306a36Sopenharmony_ci		goto err;
53262306a36Sopenharmony_ci	fp->in_ep = ep;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress;
53562306a36Sopenharmony_ci	pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* Do not try to bind Phonet twice... */
53862306a36Sopenharmony_ci	status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
53962306a36Sopenharmony_ci			NULL, NULL);
54062306a36Sopenharmony_ci	if (status)
54162306a36Sopenharmony_ci		goto err;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/* Incoming USB requests */
54462306a36Sopenharmony_ci	status = -ENOMEM;
54562306a36Sopenharmony_ci	for (i = 0; i < phonet_rxq_size; i++) {
54662306a36Sopenharmony_ci		struct usb_request *req;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL);
54962306a36Sopenharmony_ci		if (!req)
55062306a36Sopenharmony_ci			goto err_req;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		req->complete = pn_rx_complete;
55362306a36Sopenharmony_ci		fp->out_reqv[i] = req;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Outgoing USB requests */
55762306a36Sopenharmony_ci	fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL);
55862306a36Sopenharmony_ci	if (!fp->in_req)
55962306a36Sopenharmony_ci		goto err_req;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	INFO(cdev, "USB CDC Phonet function\n");
56262306a36Sopenharmony_ci	INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name,
56362306a36Sopenharmony_ci		fp->out_ep->name, fp->in_ep->name);
56462306a36Sopenharmony_ci	return 0;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cierr_req:
56762306a36Sopenharmony_ci	for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++)
56862306a36Sopenharmony_ci		usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
56962306a36Sopenharmony_ci	usb_free_all_descriptors(f);
57062306a36Sopenharmony_cierr:
57162306a36Sopenharmony_ci	ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n");
57262306a36Sopenharmony_ci	return status;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	return container_of(to_config_group(item), struct f_phonet_opts,
57862306a36Sopenharmony_ci			func_inst.group);
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic void phonet_attr_release(struct config_item *item)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct f_phonet_opts *opts = to_f_phonet_opts(item);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	usb_put_function_instance(&opts->func_inst);
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic struct configfs_item_operations phonet_item_ops = {
58962306a36Sopenharmony_ci	.release		= phonet_attr_release,
59062306a36Sopenharmony_ci};
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic ssize_t f_phonet_ifname_show(struct config_item *item, char *page)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	return gether_get_ifname(to_f_phonet_opts(item)->net, page, PAGE_SIZE);
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ciCONFIGFS_ATTR_RO(f_phonet_, ifname);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic struct configfs_attribute *phonet_attrs[] = {
60062306a36Sopenharmony_ci	&f_phonet_attr_ifname,
60162306a36Sopenharmony_ci	NULL,
60262306a36Sopenharmony_ci};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic const struct config_item_type phonet_func_type = {
60562306a36Sopenharmony_ci	.ct_item_ops	= &phonet_item_ops,
60662306a36Sopenharmony_ci	.ct_attrs	= phonet_attrs,
60762306a36Sopenharmony_ci	.ct_owner	= THIS_MODULE,
60862306a36Sopenharmony_ci};
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic void phonet_free_inst(struct usb_function_instance *f)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	struct f_phonet_opts *opts;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	opts = container_of(f, struct f_phonet_opts, func_inst);
61562306a36Sopenharmony_ci	if (opts->bound)
61662306a36Sopenharmony_ci		gphonet_cleanup(opts->net);
61762306a36Sopenharmony_ci	else
61862306a36Sopenharmony_ci		free_netdev(opts->net);
61962306a36Sopenharmony_ci	kfree(opts);
62062306a36Sopenharmony_ci}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic struct usb_function_instance *phonet_alloc_inst(void)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct f_phonet_opts *opts;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
62762306a36Sopenharmony_ci	if (!opts)
62862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	opts->func_inst.free_func_inst = phonet_free_inst;
63162306a36Sopenharmony_ci	opts->net = gphonet_setup_default();
63262306a36Sopenharmony_ci	if (IS_ERR(opts->net)) {
63362306a36Sopenharmony_ci		struct net_device *net = opts->net;
63462306a36Sopenharmony_ci		kfree(opts);
63562306a36Sopenharmony_ci		return ERR_CAST(net);
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	config_group_init_type_name(&opts->func_inst.group, "",
63962306a36Sopenharmony_ci			&phonet_func_type);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return &opts->func_inst;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic void phonet_free(struct usb_function *f)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct f_phonet *phonet;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	phonet = func_to_pn(f);
64962306a36Sopenharmony_ci	kfree(phonet);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_cistatic void pn_unbind(struct usb_configuration *c, struct usb_function *f)
65362306a36Sopenharmony_ci{
65462306a36Sopenharmony_ci	struct f_phonet *fp = func_to_pn(f);
65562306a36Sopenharmony_ci	int i;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/* We are already disconnected */
65862306a36Sopenharmony_ci	if (fp->in_req)
65962306a36Sopenharmony_ci		usb_ep_free_request(fp->in_ep, fp->in_req);
66062306a36Sopenharmony_ci	for (i = 0; i < phonet_rxq_size; i++)
66162306a36Sopenharmony_ci		if (fp->out_reqv[i])
66262306a36Sopenharmony_ci			usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	usb_free_all_descriptors(f);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic struct usb_function *phonet_alloc(struct usb_function_instance *fi)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct f_phonet *fp;
67062306a36Sopenharmony_ci	struct f_phonet_opts *opts;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL);
67362306a36Sopenharmony_ci	if (!fp)
67462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	opts = container_of(fi, struct f_phonet_opts, func_inst);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	fp->dev = opts->net;
67962306a36Sopenharmony_ci	fp->function.name = "phonet";
68062306a36Sopenharmony_ci	fp->function.bind = pn_bind;
68162306a36Sopenharmony_ci	fp->function.unbind = pn_unbind;
68262306a36Sopenharmony_ci	fp->function.set_alt = pn_set_alt;
68362306a36Sopenharmony_ci	fp->function.get_alt = pn_get_alt;
68462306a36Sopenharmony_ci	fp->function.disable = pn_disconnect;
68562306a36Sopenharmony_ci	fp->function.free_func = phonet_free;
68662306a36Sopenharmony_ci	spin_lock_init(&fp->rx.lock);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	return &fp->function;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_cistruct net_device *gphonet_setup_default(void)
69262306a36Sopenharmony_ci{
69362306a36Sopenharmony_ci	struct net_device *dev;
69462306a36Sopenharmony_ci	struct phonet_port *port;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	/* Create net device */
69762306a36Sopenharmony_ci	dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN,
69862306a36Sopenharmony_ci			   pn_net_setup);
69962306a36Sopenharmony_ci	if (!dev)
70062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	port = netdev_priv(dev);
70362306a36Sopenharmony_ci	spin_lock_init(&port->lock);
70462306a36Sopenharmony_ci	netif_carrier_off(dev);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return dev;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_civoid gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	SET_NETDEV_DEV(net, &g->dev);
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ciint gphonet_register_netdev(struct net_device *net)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	int status;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	status = register_netdev(net);
71962306a36Sopenharmony_ci	if (status)
72062306a36Sopenharmony_ci		free_netdev(net);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	return status;
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_civoid gphonet_cleanup(struct net_device *dev)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	unregister_netdev(dev);
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ciDECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
73162306a36Sopenharmony_ciMODULE_AUTHOR("Rémi Denis-Courmont");
73262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
733