15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
200a2430fSAndrzej Pietrasiewicz /*
300a2430fSAndrzej Pietrasiewicz * f_phonet.c -- USB CDC Phonet function
400a2430fSAndrzej Pietrasiewicz *
500a2430fSAndrzej Pietrasiewicz * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
600a2430fSAndrzej Pietrasiewicz *
700a2430fSAndrzej Pietrasiewicz * Author: Rémi Denis-Courmont
800a2430fSAndrzej Pietrasiewicz */
900a2430fSAndrzej Pietrasiewicz
1000a2430fSAndrzej Pietrasiewicz #include <linux/mm.h>
1100a2430fSAndrzej Pietrasiewicz #include <linux/slab.h>
1200a2430fSAndrzej Pietrasiewicz #include <linux/kernel.h>
1300a2430fSAndrzej Pietrasiewicz #include <linux/module.h>
1400a2430fSAndrzej Pietrasiewicz #include <linux/device.h>
1500a2430fSAndrzej Pietrasiewicz
1600a2430fSAndrzej Pietrasiewicz #include <linux/netdevice.h>
1700a2430fSAndrzej Pietrasiewicz #include <linux/if_ether.h>
1800a2430fSAndrzej Pietrasiewicz #include <linux/if_phonet.h>
1900a2430fSAndrzej Pietrasiewicz #include <linux/if_arp.h>
2000a2430fSAndrzej Pietrasiewicz
2100a2430fSAndrzej Pietrasiewicz #include <linux/usb/ch9.h>
2200a2430fSAndrzej Pietrasiewicz #include <linux/usb/cdc.h>
2300a2430fSAndrzej Pietrasiewicz #include <linux/usb/composite.h>
2400a2430fSAndrzej Pietrasiewicz
2500a2430fSAndrzej Pietrasiewicz #include "u_phonet.h"
2600a2430fSAndrzej Pietrasiewicz #include "u_ether.h"
2700a2430fSAndrzej Pietrasiewicz
2800a2430fSAndrzej Pietrasiewicz #define PN_MEDIA_USB 0x1B
2900a2430fSAndrzej Pietrasiewicz #define MAXPACKET 512
3000a2430fSAndrzej Pietrasiewicz #if (PAGE_SIZE % MAXPACKET)
3100a2430fSAndrzej Pietrasiewicz #error MAXPACKET must divide PAGE_SIZE!
3200a2430fSAndrzej Pietrasiewicz #endif
3300a2430fSAndrzej Pietrasiewicz
3400a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
3500a2430fSAndrzej Pietrasiewicz
3600a2430fSAndrzej Pietrasiewicz struct phonet_port {
3700a2430fSAndrzej Pietrasiewicz struct f_phonet *usb;
3800a2430fSAndrzej Pietrasiewicz spinlock_t lock;
3900a2430fSAndrzej Pietrasiewicz };
4000a2430fSAndrzej Pietrasiewicz
4100a2430fSAndrzej Pietrasiewicz struct f_phonet {
4200a2430fSAndrzej Pietrasiewicz struct usb_function function;
4300a2430fSAndrzej Pietrasiewicz struct {
4400a2430fSAndrzej Pietrasiewicz struct sk_buff *skb;
4500a2430fSAndrzej Pietrasiewicz spinlock_t lock;
4600a2430fSAndrzej Pietrasiewicz } rx;
4700a2430fSAndrzej Pietrasiewicz struct net_device *dev;
4800a2430fSAndrzej Pietrasiewicz struct usb_ep *in_ep, *out_ep;
4900a2430fSAndrzej Pietrasiewicz
5000a2430fSAndrzej Pietrasiewicz struct usb_request *in_req;
51b57a368eSGustavo A. R. Silva struct usb_request *out_reqv[];
5200a2430fSAndrzej Pietrasiewicz };
5300a2430fSAndrzej Pietrasiewicz
5400a2430fSAndrzej Pietrasiewicz static int phonet_rxq_size = 17;
5500a2430fSAndrzej Pietrasiewicz
func_to_pn(struct usb_function * f)5600a2430fSAndrzej Pietrasiewicz static inline struct f_phonet *func_to_pn(struct usb_function *f)
5700a2430fSAndrzej Pietrasiewicz {
5800a2430fSAndrzej Pietrasiewicz return container_of(f, struct f_phonet, function);
5900a2430fSAndrzej Pietrasiewicz }
6000a2430fSAndrzej Pietrasiewicz
6100a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
6200a2430fSAndrzej Pietrasiewicz
6300a2430fSAndrzej Pietrasiewicz #define USB_CDC_SUBCLASS_PHONET 0xfe
6400a2430fSAndrzej Pietrasiewicz #define USB_CDC_PHONET_TYPE 0xab
6500a2430fSAndrzej Pietrasiewicz
6600a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor
6700a2430fSAndrzej Pietrasiewicz pn_control_intf_desc = {
6800a2430fSAndrzej Pietrasiewicz .bLength = sizeof pn_control_intf_desc,
6900a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE,
7000a2430fSAndrzej Pietrasiewicz
7100a2430fSAndrzej Pietrasiewicz /* .bInterfaceNumber = DYNAMIC, */
7200a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_COMM,
7300a2430fSAndrzej Pietrasiewicz .bInterfaceSubClass = USB_CDC_SUBCLASS_PHONET,
7400a2430fSAndrzej Pietrasiewicz };
7500a2430fSAndrzej Pietrasiewicz
7600a2430fSAndrzej Pietrasiewicz static const struct usb_cdc_header_desc
7700a2430fSAndrzej Pietrasiewicz pn_header_desc = {
7800a2430fSAndrzej Pietrasiewicz .bLength = sizeof pn_header_desc,
7900a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_CS_INTERFACE,
8000a2430fSAndrzej Pietrasiewicz .bDescriptorSubType = USB_CDC_HEADER_TYPE,
8100a2430fSAndrzej Pietrasiewicz .bcdCDC = cpu_to_le16(0x0110),
8200a2430fSAndrzej Pietrasiewicz };
8300a2430fSAndrzej Pietrasiewicz
8400a2430fSAndrzej Pietrasiewicz static const struct usb_cdc_header_desc
8500a2430fSAndrzej Pietrasiewicz pn_phonet_desc = {
8600a2430fSAndrzej Pietrasiewicz .bLength = sizeof pn_phonet_desc,
8700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_CS_INTERFACE,
8800a2430fSAndrzej Pietrasiewicz .bDescriptorSubType = USB_CDC_PHONET_TYPE,
8900a2430fSAndrzej Pietrasiewicz .bcdCDC = cpu_to_le16(0x1505), /* ??? */
9000a2430fSAndrzej Pietrasiewicz };
9100a2430fSAndrzej Pietrasiewicz
9200a2430fSAndrzej Pietrasiewicz static struct usb_cdc_union_desc
9300a2430fSAndrzej Pietrasiewicz pn_union_desc = {
9400a2430fSAndrzej Pietrasiewicz .bLength = sizeof pn_union_desc,
9500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_CS_INTERFACE,
9600a2430fSAndrzej Pietrasiewicz .bDescriptorSubType = USB_CDC_UNION_TYPE,
9700a2430fSAndrzej Pietrasiewicz
9800a2430fSAndrzej Pietrasiewicz /* .bMasterInterface0 = DYNAMIC, */
9900a2430fSAndrzej Pietrasiewicz /* .bSlaveInterface0 = DYNAMIC, */
10000a2430fSAndrzej Pietrasiewicz };
10100a2430fSAndrzej Pietrasiewicz
10200a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor
10300a2430fSAndrzej Pietrasiewicz pn_data_nop_intf_desc = {
10400a2430fSAndrzej Pietrasiewicz .bLength = sizeof pn_data_nop_intf_desc,
10500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE,
10600a2430fSAndrzej Pietrasiewicz
10700a2430fSAndrzej Pietrasiewicz /* .bInterfaceNumber = DYNAMIC, */
10800a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 0,
10900a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 0,
11000a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_CDC_DATA,
11100a2430fSAndrzej Pietrasiewicz };
11200a2430fSAndrzej Pietrasiewicz
11300a2430fSAndrzej Pietrasiewicz static struct usb_interface_descriptor
11400a2430fSAndrzej Pietrasiewicz pn_data_intf_desc = {
11500a2430fSAndrzej Pietrasiewicz .bLength = sizeof pn_data_intf_desc,
11600a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_INTERFACE,
11700a2430fSAndrzej Pietrasiewicz
11800a2430fSAndrzej Pietrasiewicz /* .bInterfaceNumber = DYNAMIC, */
11900a2430fSAndrzej Pietrasiewicz .bAlternateSetting = 1,
12000a2430fSAndrzej Pietrasiewicz .bNumEndpoints = 2,
12100a2430fSAndrzej Pietrasiewicz .bInterfaceClass = USB_CLASS_CDC_DATA,
12200a2430fSAndrzej Pietrasiewicz };
12300a2430fSAndrzej Pietrasiewicz
12400a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor
12500a2430fSAndrzej Pietrasiewicz pn_fs_sink_desc = {
12600a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
12700a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
12800a2430fSAndrzej Pietrasiewicz
12900a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT,
13000a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
13100a2430fSAndrzej Pietrasiewicz };
13200a2430fSAndrzej Pietrasiewicz
13300a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor
13400a2430fSAndrzej Pietrasiewicz pn_hs_sink_desc = {
13500a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
13600a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
13700a2430fSAndrzej Pietrasiewicz
13800a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT,
13900a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
14000a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(MAXPACKET),
14100a2430fSAndrzej Pietrasiewicz };
14200a2430fSAndrzej Pietrasiewicz
14300a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor
14400a2430fSAndrzej Pietrasiewicz pn_fs_source_desc = {
14500a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
14600a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
14700a2430fSAndrzej Pietrasiewicz
14800a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN,
14900a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
15000a2430fSAndrzej Pietrasiewicz };
15100a2430fSAndrzej Pietrasiewicz
15200a2430fSAndrzej Pietrasiewicz static struct usb_endpoint_descriptor
15300a2430fSAndrzej Pietrasiewicz pn_hs_source_desc = {
15400a2430fSAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
15500a2430fSAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
15600a2430fSAndrzej Pietrasiewicz
15700a2430fSAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN,
15800a2430fSAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
15900a2430fSAndrzej Pietrasiewicz .wMaxPacketSize = cpu_to_le16(512),
16000a2430fSAndrzej Pietrasiewicz };
16100a2430fSAndrzej Pietrasiewicz
16200a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *fs_pn_function[] = {
16300a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_control_intf_desc,
16400a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_header_desc,
16500a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_phonet_desc,
16600a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_union_desc,
16700a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
16800a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_data_intf_desc,
16900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_fs_sink_desc,
17000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_fs_source_desc,
17100a2430fSAndrzej Pietrasiewicz NULL,
17200a2430fSAndrzej Pietrasiewicz };
17300a2430fSAndrzej Pietrasiewicz
17400a2430fSAndrzej Pietrasiewicz static struct usb_descriptor_header *hs_pn_function[] = {
17500a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_control_intf_desc,
17600a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_header_desc,
17700a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_phonet_desc,
17800a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_union_desc,
17900a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_data_nop_intf_desc,
18000a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_data_intf_desc,
18100a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_hs_sink_desc,
18200a2430fSAndrzej Pietrasiewicz (struct usb_descriptor_header *) &pn_hs_source_desc,
18300a2430fSAndrzej Pietrasiewicz NULL,
18400a2430fSAndrzej Pietrasiewicz };
18500a2430fSAndrzej Pietrasiewicz
18600a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
18700a2430fSAndrzej Pietrasiewicz
pn_net_open(struct net_device * dev)18800a2430fSAndrzej Pietrasiewicz static int pn_net_open(struct net_device *dev)
18900a2430fSAndrzej Pietrasiewicz {
19000a2430fSAndrzej Pietrasiewicz netif_wake_queue(dev);
19100a2430fSAndrzej Pietrasiewicz return 0;
19200a2430fSAndrzej Pietrasiewicz }
19300a2430fSAndrzej Pietrasiewicz
pn_net_close(struct net_device * dev)19400a2430fSAndrzej Pietrasiewicz static int pn_net_close(struct net_device *dev)
19500a2430fSAndrzej Pietrasiewicz {
19600a2430fSAndrzej Pietrasiewicz netif_stop_queue(dev);
19700a2430fSAndrzej Pietrasiewicz return 0;
19800a2430fSAndrzej Pietrasiewicz }
19900a2430fSAndrzej Pietrasiewicz
pn_tx_complete(struct usb_ep * ep,struct usb_request * req)20000a2430fSAndrzej Pietrasiewicz static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req)
20100a2430fSAndrzej Pietrasiewicz {
20200a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = ep->driver_data;
20300a2430fSAndrzej Pietrasiewicz struct net_device *dev = fp->dev;
20400a2430fSAndrzej Pietrasiewicz struct sk_buff *skb = req->context;
20500a2430fSAndrzej Pietrasiewicz
20600a2430fSAndrzej Pietrasiewicz switch (req->status) {
20700a2430fSAndrzej Pietrasiewicz case 0:
20800a2430fSAndrzej Pietrasiewicz dev->stats.tx_packets++;
20900a2430fSAndrzej Pietrasiewicz dev->stats.tx_bytes += skb->len;
21000a2430fSAndrzej Pietrasiewicz break;
21100a2430fSAndrzej Pietrasiewicz
21200a2430fSAndrzej Pietrasiewicz case -ESHUTDOWN: /* disconnected */
21300a2430fSAndrzej Pietrasiewicz case -ECONNRESET: /* disabled */
21400a2430fSAndrzej Pietrasiewicz dev->stats.tx_aborted_errors++;
215a74005abSGustavo A. R. Silva fallthrough;
21600a2430fSAndrzej Pietrasiewicz default:
21700a2430fSAndrzej Pietrasiewicz dev->stats.tx_errors++;
21800a2430fSAndrzej Pietrasiewicz }
21900a2430fSAndrzej Pietrasiewicz
22000a2430fSAndrzej Pietrasiewicz dev_kfree_skb_any(skb);
22100a2430fSAndrzej Pietrasiewicz netif_wake_queue(dev);
22200a2430fSAndrzej Pietrasiewicz }
22300a2430fSAndrzej Pietrasiewicz
pn_net_xmit(struct sk_buff * skb,struct net_device * dev)224064b4075SLuc Van Oostenryck static netdev_tx_t pn_net_xmit(struct sk_buff *skb, struct net_device *dev)
22500a2430fSAndrzej Pietrasiewicz {
22600a2430fSAndrzej Pietrasiewicz struct phonet_port *port = netdev_priv(dev);
22700a2430fSAndrzej Pietrasiewicz struct f_phonet *fp;
22800a2430fSAndrzej Pietrasiewicz struct usb_request *req;
22900a2430fSAndrzej Pietrasiewicz unsigned long flags;
23000a2430fSAndrzej Pietrasiewicz
23100a2430fSAndrzej Pietrasiewicz if (skb->protocol != htons(ETH_P_PHONET))
23200a2430fSAndrzej Pietrasiewicz goto out;
23300a2430fSAndrzej Pietrasiewicz
23400a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&port->lock, flags);
23500a2430fSAndrzej Pietrasiewicz fp = port->usb;
23600a2430fSAndrzej Pietrasiewicz if (unlikely(!fp)) /* race with carrier loss */
23700a2430fSAndrzej Pietrasiewicz goto out_unlock;
23800a2430fSAndrzej Pietrasiewicz
23900a2430fSAndrzej Pietrasiewicz req = fp->in_req;
24000a2430fSAndrzej Pietrasiewicz req->buf = skb->data;
24100a2430fSAndrzej Pietrasiewicz req->length = skb->len;
24200a2430fSAndrzej Pietrasiewicz req->complete = pn_tx_complete;
24300a2430fSAndrzej Pietrasiewicz req->zero = 1;
24400a2430fSAndrzej Pietrasiewicz req->context = skb;
24500a2430fSAndrzej Pietrasiewicz
24600a2430fSAndrzej Pietrasiewicz if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC)))
24700a2430fSAndrzej Pietrasiewicz goto out_unlock;
24800a2430fSAndrzej Pietrasiewicz
24900a2430fSAndrzej Pietrasiewicz netif_stop_queue(dev);
25000a2430fSAndrzej Pietrasiewicz skb = NULL;
25100a2430fSAndrzej Pietrasiewicz
25200a2430fSAndrzej Pietrasiewicz out_unlock:
25300a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&port->lock, flags);
25400a2430fSAndrzej Pietrasiewicz out:
25500a2430fSAndrzej Pietrasiewicz if (unlikely(skb)) {
25600a2430fSAndrzej Pietrasiewicz dev_kfree_skb(skb);
25700a2430fSAndrzej Pietrasiewicz dev->stats.tx_dropped++;
25800a2430fSAndrzej Pietrasiewicz }
25900a2430fSAndrzej Pietrasiewicz return NETDEV_TX_OK;
26000a2430fSAndrzej Pietrasiewicz }
26100a2430fSAndrzej Pietrasiewicz
26200a2430fSAndrzej Pietrasiewicz static const struct net_device_ops pn_netdev_ops = {
26300a2430fSAndrzej Pietrasiewicz .ndo_open = pn_net_open,
26400a2430fSAndrzej Pietrasiewicz .ndo_stop = pn_net_close,
26500a2430fSAndrzej Pietrasiewicz .ndo_start_xmit = pn_net_xmit,
26600a2430fSAndrzej Pietrasiewicz };
26700a2430fSAndrzej Pietrasiewicz
pn_net_setup(struct net_device * dev)26800a2430fSAndrzej Pietrasiewicz static void pn_net_setup(struct net_device *dev)
26900a2430fSAndrzej Pietrasiewicz {
27013b5ffa0SJakub Kicinski const u8 addr = PN_MEDIA_USB;
27113b5ffa0SJakub Kicinski
27200a2430fSAndrzej Pietrasiewicz dev->features = 0;
27300a2430fSAndrzej Pietrasiewicz dev->type = ARPHRD_PHONET;
27400a2430fSAndrzej Pietrasiewicz dev->flags = IFF_POINTOPOINT | IFF_NOARP;
27500a2430fSAndrzej Pietrasiewicz dev->mtu = PHONET_DEV_MTU;
276b3e3893eSJarod Wilson dev->min_mtu = PHONET_MIN_MTU;
277b3e3893eSJarod Wilson dev->max_mtu = PHONET_MAX_MTU;
27800a2430fSAndrzej Pietrasiewicz dev->hard_header_len = 1;
27900a2430fSAndrzej Pietrasiewicz dev->addr_len = 1;
28013b5ffa0SJakub Kicinski dev_addr_set(dev, &addr);
28113b5ffa0SJakub Kicinski
28200a2430fSAndrzej Pietrasiewicz dev->tx_queue_len = 1;
28300a2430fSAndrzej Pietrasiewicz
28400a2430fSAndrzej Pietrasiewicz dev->netdev_ops = &pn_netdev_ops;
285cf124db5SDavid S. Miller dev->needs_free_netdev = true;
28600a2430fSAndrzej Pietrasiewicz dev->header_ops = &phonet_header_ops;
28700a2430fSAndrzej Pietrasiewicz }
28800a2430fSAndrzej Pietrasiewicz
28900a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
29000a2430fSAndrzej Pietrasiewicz
29100a2430fSAndrzej Pietrasiewicz /*
29200a2430fSAndrzej Pietrasiewicz * Queue buffer for data from the host
29300a2430fSAndrzej Pietrasiewicz */
29400a2430fSAndrzej Pietrasiewicz static int
pn_rx_submit(struct f_phonet * fp,struct usb_request * req,gfp_t gfp_flags)29500a2430fSAndrzej Pietrasiewicz pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags)
29600a2430fSAndrzej Pietrasiewicz {
29700a2430fSAndrzej Pietrasiewicz struct page *page;
29800a2430fSAndrzej Pietrasiewicz int err;
29900a2430fSAndrzej Pietrasiewicz
3005693d284SAlexander Duyck page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC);
30100a2430fSAndrzej Pietrasiewicz if (!page)
30200a2430fSAndrzej Pietrasiewicz return -ENOMEM;
30300a2430fSAndrzej Pietrasiewicz
30400a2430fSAndrzej Pietrasiewicz req->buf = page_address(page);
30500a2430fSAndrzej Pietrasiewicz req->length = PAGE_SIZE;
30600a2430fSAndrzej Pietrasiewicz req->context = page;
30700a2430fSAndrzej Pietrasiewicz
30800a2430fSAndrzej Pietrasiewicz err = usb_ep_queue(fp->out_ep, req, gfp_flags);
30900a2430fSAndrzej Pietrasiewicz if (unlikely(err))
31000a2430fSAndrzej Pietrasiewicz put_page(page);
31100a2430fSAndrzej Pietrasiewicz return err;
31200a2430fSAndrzej Pietrasiewicz }
31300a2430fSAndrzej Pietrasiewicz
pn_rx_complete(struct usb_ep * ep,struct usb_request * req)31400a2430fSAndrzej Pietrasiewicz static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
31500a2430fSAndrzej Pietrasiewicz {
31600a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = ep->driver_data;
31700a2430fSAndrzej Pietrasiewicz struct net_device *dev = fp->dev;
31800a2430fSAndrzej Pietrasiewicz struct page *page = req->context;
31900a2430fSAndrzej Pietrasiewicz struct sk_buff *skb;
32000a2430fSAndrzej Pietrasiewicz unsigned long flags;
32100a2430fSAndrzej Pietrasiewicz int status = req->status;
32200a2430fSAndrzej Pietrasiewicz
32300a2430fSAndrzej Pietrasiewicz switch (status) {
32400a2430fSAndrzej Pietrasiewicz case 0:
32500a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&fp->rx.lock, flags);
32600a2430fSAndrzej Pietrasiewicz skb = fp->rx.skb;
32700a2430fSAndrzej Pietrasiewicz if (!skb)
32800a2430fSAndrzej Pietrasiewicz skb = fp->rx.skb = netdev_alloc_skb(dev, 12);
32900a2430fSAndrzej Pietrasiewicz if (req->actual < req->length) /* Last fragment */
33000a2430fSAndrzej Pietrasiewicz fp->rx.skb = NULL;
33100a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&fp->rx.lock, flags);
33200a2430fSAndrzej Pietrasiewicz
33300a2430fSAndrzej Pietrasiewicz if (unlikely(!skb))
33400a2430fSAndrzej Pietrasiewicz break;
33500a2430fSAndrzej Pietrasiewicz
33600a2430fSAndrzej Pietrasiewicz if (skb->len == 0) { /* First fragment */
33700a2430fSAndrzej Pietrasiewicz skb->protocol = htons(ETH_P_PHONET);
33800a2430fSAndrzej Pietrasiewicz skb_reset_mac_header(skb);
33900a2430fSAndrzej Pietrasiewicz /* Can't use pskb_pull() on page in IRQ */
34059ae1d12SJohannes Berg skb_put_data(skb, page_address(page), 1);
34100a2430fSAndrzej Pietrasiewicz }
34200a2430fSAndrzej Pietrasiewicz
34300a2430fSAndrzej Pietrasiewicz skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
34400a2430fSAndrzej Pietrasiewicz skb->len <= 1, req->actual, PAGE_SIZE);
34500a2430fSAndrzej Pietrasiewicz page = NULL;
34600a2430fSAndrzej Pietrasiewicz
34700a2430fSAndrzej Pietrasiewicz if (req->actual < req->length) { /* Last fragment */
34800a2430fSAndrzej Pietrasiewicz skb->dev = dev;
34900a2430fSAndrzej Pietrasiewicz dev->stats.rx_packets++;
35000a2430fSAndrzej Pietrasiewicz dev->stats.rx_bytes += skb->len;
35100a2430fSAndrzej Pietrasiewicz
35200a2430fSAndrzej Pietrasiewicz netif_rx(skb);
35300a2430fSAndrzej Pietrasiewicz }
35400a2430fSAndrzej Pietrasiewicz break;
35500a2430fSAndrzej Pietrasiewicz
35600a2430fSAndrzej Pietrasiewicz /* Do not resubmit in these cases: */
35700a2430fSAndrzej Pietrasiewicz case -ESHUTDOWN: /* disconnect */
35800a2430fSAndrzej Pietrasiewicz case -ECONNABORTED: /* hw reset */
35900a2430fSAndrzej Pietrasiewicz case -ECONNRESET: /* dequeued (unlink or netif down) */
36000a2430fSAndrzej Pietrasiewicz req = NULL;
36100a2430fSAndrzej Pietrasiewicz break;
36200a2430fSAndrzej Pietrasiewicz
36300a2430fSAndrzej Pietrasiewicz /* Do resubmit in these cases: */
36400a2430fSAndrzej Pietrasiewicz case -EOVERFLOW: /* request buffer overflow */
36500a2430fSAndrzej Pietrasiewicz dev->stats.rx_over_errors++;
366a74005abSGustavo A. R. Silva fallthrough;
36700a2430fSAndrzej Pietrasiewicz default:
36800a2430fSAndrzej Pietrasiewicz dev->stats.rx_errors++;
36900a2430fSAndrzej Pietrasiewicz break;
37000a2430fSAndrzej Pietrasiewicz }
37100a2430fSAndrzej Pietrasiewicz
37200a2430fSAndrzej Pietrasiewicz if (page)
37300a2430fSAndrzej Pietrasiewicz put_page(page);
37400a2430fSAndrzej Pietrasiewicz if (req)
3755693d284SAlexander Duyck pn_rx_submit(fp, req, GFP_ATOMIC);
37600a2430fSAndrzej Pietrasiewicz }
37700a2430fSAndrzej Pietrasiewicz
37800a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
37900a2430fSAndrzej Pietrasiewicz
__pn_reset(struct usb_function * f)38000a2430fSAndrzej Pietrasiewicz static void __pn_reset(struct usb_function *f)
38100a2430fSAndrzej Pietrasiewicz {
38200a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = func_to_pn(f);
38300a2430fSAndrzej Pietrasiewicz struct net_device *dev = fp->dev;
38400a2430fSAndrzej Pietrasiewicz struct phonet_port *port = netdev_priv(dev);
38500a2430fSAndrzej Pietrasiewicz
38600a2430fSAndrzej Pietrasiewicz netif_carrier_off(dev);
38700a2430fSAndrzej Pietrasiewicz port->usb = NULL;
38800a2430fSAndrzej Pietrasiewicz
38900a2430fSAndrzej Pietrasiewicz usb_ep_disable(fp->out_ep);
39000a2430fSAndrzej Pietrasiewicz usb_ep_disable(fp->in_ep);
39100a2430fSAndrzej Pietrasiewicz if (fp->rx.skb) {
39200a2430fSAndrzej Pietrasiewicz dev_kfree_skb_irq(fp->rx.skb);
39300a2430fSAndrzej Pietrasiewicz fp->rx.skb = NULL;
39400a2430fSAndrzej Pietrasiewicz }
39500a2430fSAndrzej Pietrasiewicz }
39600a2430fSAndrzej Pietrasiewicz
pn_set_alt(struct usb_function * f,unsigned intf,unsigned alt)39700a2430fSAndrzej Pietrasiewicz static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
39800a2430fSAndrzej Pietrasiewicz {
39900a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = func_to_pn(f);
40000a2430fSAndrzej Pietrasiewicz struct usb_gadget *gadget = fp->function.config->cdev->gadget;
40100a2430fSAndrzej Pietrasiewicz
40200a2430fSAndrzej Pietrasiewicz if (intf == pn_control_intf_desc.bInterfaceNumber)
40300a2430fSAndrzej Pietrasiewicz /* control interface, no altsetting */
40400a2430fSAndrzej Pietrasiewicz return (alt > 0) ? -EINVAL : 0;
40500a2430fSAndrzej Pietrasiewicz
40600a2430fSAndrzej Pietrasiewicz if (intf == pn_data_intf_desc.bInterfaceNumber) {
40700a2430fSAndrzej Pietrasiewicz struct net_device *dev = fp->dev;
40800a2430fSAndrzej Pietrasiewicz struct phonet_port *port = netdev_priv(dev);
40900a2430fSAndrzej Pietrasiewicz
41000a2430fSAndrzej Pietrasiewicz /* data intf (0: inactive, 1: active) */
41100a2430fSAndrzej Pietrasiewicz if (alt > 1)
41200a2430fSAndrzej Pietrasiewicz return -EINVAL;
41300a2430fSAndrzej Pietrasiewicz
41400a2430fSAndrzej Pietrasiewicz spin_lock(&port->lock);
4159ec36f7fSFelipe Balbi
416101382ffSRobert Baldyga if (fp->in_ep->enabled)
41700a2430fSAndrzej Pietrasiewicz __pn_reset(f);
4189ec36f7fSFelipe Balbi
41900a2430fSAndrzej Pietrasiewicz if (alt == 1) {
42000a2430fSAndrzej Pietrasiewicz int i;
42100a2430fSAndrzej Pietrasiewicz
42200a2430fSAndrzej Pietrasiewicz if (config_ep_by_speed(gadget, f, fp->in_ep) ||
42300a2430fSAndrzej Pietrasiewicz config_ep_by_speed(gadget, f, fp->out_ep)) {
42400a2430fSAndrzej Pietrasiewicz fp->in_ep->desc = NULL;
42500a2430fSAndrzej Pietrasiewicz fp->out_ep->desc = NULL;
42600a2430fSAndrzej Pietrasiewicz spin_unlock(&port->lock);
42700a2430fSAndrzej Pietrasiewicz return -EINVAL;
42800a2430fSAndrzej Pietrasiewicz }
42900a2430fSAndrzej Pietrasiewicz usb_ep_enable(fp->out_ep);
43000a2430fSAndrzej Pietrasiewicz usb_ep_enable(fp->in_ep);
43100a2430fSAndrzej Pietrasiewicz
43200a2430fSAndrzej Pietrasiewicz port->usb = fp;
43300a2430fSAndrzej Pietrasiewicz fp->out_ep->driver_data = fp;
43400a2430fSAndrzej Pietrasiewicz fp->in_ep->driver_data = fp;
43500a2430fSAndrzej Pietrasiewicz
43600a2430fSAndrzej Pietrasiewicz netif_carrier_on(dev);
43700a2430fSAndrzej Pietrasiewicz for (i = 0; i < phonet_rxq_size; i++)
4385693d284SAlexander Duyck pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC);
43900a2430fSAndrzej Pietrasiewicz }
44000a2430fSAndrzej Pietrasiewicz spin_unlock(&port->lock);
44100a2430fSAndrzej Pietrasiewicz return 0;
44200a2430fSAndrzej Pietrasiewicz }
44300a2430fSAndrzej Pietrasiewicz
44400a2430fSAndrzej Pietrasiewicz return -EINVAL;
44500a2430fSAndrzej Pietrasiewicz }
44600a2430fSAndrzej Pietrasiewicz
pn_get_alt(struct usb_function * f,unsigned intf)44700a2430fSAndrzej Pietrasiewicz static int pn_get_alt(struct usb_function *f, unsigned intf)
44800a2430fSAndrzej Pietrasiewicz {
44900a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = func_to_pn(f);
45000a2430fSAndrzej Pietrasiewicz
45100a2430fSAndrzej Pietrasiewicz if (intf == pn_control_intf_desc.bInterfaceNumber)
45200a2430fSAndrzej Pietrasiewicz return 0;
45300a2430fSAndrzej Pietrasiewicz
45400a2430fSAndrzej Pietrasiewicz if (intf == pn_data_intf_desc.bInterfaceNumber) {
45500a2430fSAndrzej Pietrasiewicz struct phonet_port *port = netdev_priv(fp->dev);
45600a2430fSAndrzej Pietrasiewicz u8 alt;
45700a2430fSAndrzej Pietrasiewicz
45800a2430fSAndrzej Pietrasiewicz spin_lock(&port->lock);
45900a2430fSAndrzej Pietrasiewicz alt = port->usb != NULL;
46000a2430fSAndrzej Pietrasiewicz spin_unlock(&port->lock);
46100a2430fSAndrzej Pietrasiewicz return alt;
46200a2430fSAndrzej Pietrasiewicz }
46300a2430fSAndrzej Pietrasiewicz
46400a2430fSAndrzej Pietrasiewicz return -EINVAL;
46500a2430fSAndrzej Pietrasiewicz }
46600a2430fSAndrzej Pietrasiewicz
pn_disconnect(struct usb_function * f)46700a2430fSAndrzej Pietrasiewicz static void pn_disconnect(struct usb_function *f)
46800a2430fSAndrzej Pietrasiewicz {
46900a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = func_to_pn(f);
47000a2430fSAndrzej Pietrasiewicz struct phonet_port *port = netdev_priv(fp->dev);
47100a2430fSAndrzej Pietrasiewicz unsigned long flags;
47200a2430fSAndrzej Pietrasiewicz
47300a2430fSAndrzej Pietrasiewicz /* remain disabled until set_alt */
47400a2430fSAndrzej Pietrasiewicz spin_lock_irqsave(&port->lock, flags);
47500a2430fSAndrzej Pietrasiewicz __pn_reset(f);
47600a2430fSAndrzej Pietrasiewicz spin_unlock_irqrestore(&port->lock, flags);
47700a2430fSAndrzej Pietrasiewicz }
47800a2430fSAndrzej Pietrasiewicz
47900a2430fSAndrzej Pietrasiewicz /*-------------------------------------------------------------------------*/
48000a2430fSAndrzej Pietrasiewicz
pn_bind(struct usb_configuration * c,struct usb_function * f)48100a2430fSAndrzej Pietrasiewicz static int pn_bind(struct usb_configuration *c, struct usb_function *f)
48200a2430fSAndrzej Pietrasiewicz {
48300a2430fSAndrzej Pietrasiewicz struct usb_composite_dev *cdev = c->cdev;
48400a2430fSAndrzej Pietrasiewicz struct usb_gadget *gadget = cdev->gadget;
48500a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = func_to_pn(f);
48600a2430fSAndrzej Pietrasiewicz struct usb_ep *ep;
48700a2430fSAndrzej Pietrasiewicz int status, i;
48800a2430fSAndrzej Pietrasiewicz
48900a2430fSAndrzej Pietrasiewicz struct f_phonet_opts *phonet_opts;
49000a2430fSAndrzej Pietrasiewicz
49100a2430fSAndrzej Pietrasiewicz phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
49200a2430fSAndrzej Pietrasiewicz
49300a2430fSAndrzej Pietrasiewicz /*
49400a2430fSAndrzej Pietrasiewicz * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
49500a2430fSAndrzej Pietrasiewicz * configurations are bound in sequence with list_for_each_entry,
49600a2430fSAndrzej Pietrasiewicz * in each configuration its functions are bound in sequence
49700a2430fSAndrzej Pietrasiewicz * with list_for_each_entry, so we assume no race condition
49800a2430fSAndrzej Pietrasiewicz * with regard to phonet_opts->bound access
49900a2430fSAndrzej Pietrasiewicz */
50000a2430fSAndrzej Pietrasiewicz if (!phonet_opts->bound) {
50100a2430fSAndrzej Pietrasiewicz gphonet_set_gadget(phonet_opts->net, gadget);
50200a2430fSAndrzej Pietrasiewicz status = gphonet_register_netdev(phonet_opts->net);
50300a2430fSAndrzej Pietrasiewicz if (status)
50400a2430fSAndrzej Pietrasiewicz return status;
50500a2430fSAndrzej Pietrasiewicz phonet_opts->bound = true;
50600a2430fSAndrzej Pietrasiewicz }
50700a2430fSAndrzej Pietrasiewicz
50800a2430fSAndrzej Pietrasiewicz /* Reserve interface IDs */
50900a2430fSAndrzej Pietrasiewicz status = usb_interface_id(c, f);
51000a2430fSAndrzej Pietrasiewicz if (status < 0)
51100a2430fSAndrzej Pietrasiewicz goto err;
51200a2430fSAndrzej Pietrasiewicz pn_control_intf_desc.bInterfaceNumber = status;
51300a2430fSAndrzej Pietrasiewicz pn_union_desc.bMasterInterface0 = status;
51400a2430fSAndrzej Pietrasiewicz
51500a2430fSAndrzej Pietrasiewicz status = usb_interface_id(c, f);
51600a2430fSAndrzej Pietrasiewicz if (status < 0)
51700a2430fSAndrzej Pietrasiewicz goto err;
51800a2430fSAndrzej Pietrasiewicz pn_data_nop_intf_desc.bInterfaceNumber = status;
51900a2430fSAndrzej Pietrasiewicz pn_data_intf_desc.bInterfaceNumber = status;
52000a2430fSAndrzej Pietrasiewicz pn_union_desc.bSlaveInterface0 = status;
52100a2430fSAndrzej Pietrasiewicz
52200a2430fSAndrzej Pietrasiewicz /* Reserve endpoints */
52300a2430fSAndrzej Pietrasiewicz status = -ENODEV;
52400a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc);
52500a2430fSAndrzej Pietrasiewicz if (!ep)
52600a2430fSAndrzej Pietrasiewicz goto err;
52700a2430fSAndrzej Pietrasiewicz fp->out_ep = ep;
52800a2430fSAndrzej Pietrasiewicz
52900a2430fSAndrzej Pietrasiewicz ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc);
53000a2430fSAndrzej Pietrasiewicz if (!ep)
53100a2430fSAndrzej Pietrasiewicz goto err;
53200a2430fSAndrzej Pietrasiewicz fp->in_ep = ep;
53300a2430fSAndrzej Pietrasiewicz
53400a2430fSAndrzej Pietrasiewicz pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress;
53500a2430fSAndrzej Pietrasiewicz pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress;
53600a2430fSAndrzej Pietrasiewicz
53700a2430fSAndrzej Pietrasiewicz /* Do not try to bind Phonet twice... */
53800a2430fSAndrzej Pietrasiewicz status = usb_assign_descriptors(f, fs_pn_function, hs_pn_function,
539eaef50c7SJohn Youn NULL, NULL);
54000a2430fSAndrzej Pietrasiewicz if (status)
54100a2430fSAndrzej Pietrasiewicz goto err;
54200a2430fSAndrzej Pietrasiewicz
54300a2430fSAndrzej Pietrasiewicz /* Incoming USB requests */
54400a2430fSAndrzej Pietrasiewicz status = -ENOMEM;
54500a2430fSAndrzej Pietrasiewicz for (i = 0; i < phonet_rxq_size; i++) {
54600a2430fSAndrzej Pietrasiewicz struct usb_request *req;
54700a2430fSAndrzej Pietrasiewicz
54800a2430fSAndrzej Pietrasiewicz req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL);
54900a2430fSAndrzej Pietrasiewicz if (!req)
55000a2430fSAndrzej Pietrasiewicz goto err_req;
55100a2430fSAndrzej Pietrasiewicz
55200a2430fSAndrzej Pietrasiewicz req->complete = pn_rx_complete;
55300a2430fSAndrzej Pietrasiewicz fp->out_reqv[i] = req;
55400a2430fSAndrzej Pietrasiewicz }
55500a2430fSAndrzej Pietrasiewicz
55600a2430fSAndrzej Pietrasiewicz /* Outgoing USB requests */
55700a2430fSAndrzej Pietrasiewicz fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL);
55800a2430fSAndrzej Pietrasiewicz if (!fp->in_req)
55900a2430fSAndrzej Pietrasiewicz goto err_req;
56000a2430fSAndrzej Pietrasiewicz
56100a2430fSAndrzej Pietrasiewicz INFO(cdev, "USB CDC Phonet function\n");
56200a2430fSAndrzej Pietrasiewicz INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name,
56300a2430fSAndrzej Pietrasiewicz fp->out_ep->name, fp->in_ep->name);
56400a2430fSAndrzej Pietrasiewicz return 0;
56500a2430fSAndrzej Pietrasiewicz
56600a2430fSAndrzej Pietrasiewicz err_req:
56700a2430fSAndrzej Pietrasiewicz for (i = 0; i < phonet_rxq_size && fp->out_reqv[i]; i++)
56800a2430fSAndrzej Pietrasiewicz usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
56900a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f);
570d12a8727SPavitrakumar Managutte err:
57100a2430fSAndrzej Pietrasiewicz ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n");
57200a2430fSAndrzej Pietrasiewicz return status;
57300a2430fSAndrzej Pietrasiewicz }
57400a2430fSAndrzej Pietrasiewicz
to_f_phonet_opts(struct config_item * item)57500a2430fSAndrzej Pietrasiewicz static inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item)
57600a2430fSAndrzej Pietrasiewicz {
57700a2430fSAndrzej Pietrasiewicz return container_of(to_config_group(item), struct f_phonet_opts,
57800a2430fSAndrzej Pietrasiewicz func_inst.group);
57900a2430fSAndrzej Pietrasiewicz }
58000a2430fSAndrzej Pietrasiewicz
phonet_attr_release(struct config_item * item)58100a2430fSAndrzej Pietrasiewicz static void phonet_attr_release(struct config_item *item)
58200a2430fSAndrzej Pietrasiewicz {
58300a2430fSAndrzej Pietrasiewicz struct f_phonet_opts *opts = to_f_phonet_opts(item);
58400a2430fSAndrzej Pietrasiewicz
58500a2430fSAndrzej Pietrasiewicz usb_put_function_instance(&opts->func_inst);
58600a2430fSAndrzej Pietrasiewicz }
58700a2430fSAndrzej Pietrasiewicz
58800a2430fSAndrzej Pietrasiewicz static struct configfs_item_operations phonet_item_ops = {
58900a2430fSAndrzej Pietrasiewicz .release = phonet_attr_release,
59000a2430fSAndrzej Pietrasiewicz };
59100a2430fSAndrzej Pietrasiewicz
f_phonet_ifname_show(struct config_item * item,char * page)5920736390bSChristoph Hellwig static ssize_t f_phonet_ifname_show(struct config_item *item, char *page)
59300a2430fSAndrzej Pietrasiewicz {
5940736390bSChristoph Hellwig return gether_get_ifname(to_f_phonet_opts(item)->net, page, PAGE_SIZE);
59500a2430fSAndrzej Pietrasiewicz }
59600a2430fSAndrzej Pietrasiewicz
5970736390bSChristoph Hellwig CONFIGFS_ATTR_RO(f_phonet_, ifname);
59800a2430fSAndrzej Pietrasiewicz
59900a2430fSAndrzej Pietrasiewicz static struct configfs_attribute *phonet_attrs[] = {
6000736390bSChristoph Hellwig &f_phonet_attr_ifname,
60100a2430fSAndrzej Pietrasiewicz NULL,
60200a2430fSAndrzej Pietrasiewicz };
60300a2430fSAndrzej Pietrasiewicz
60497363902SBhumika Goyal static const struct config_item_type phonet_func_type = {
60500a2430fSAndrzej Pietrasiewicz .ct_item_ops = &phonet_item_ops,
60600a2430fSAndrzej Pietrasiewicz .ct_attrs = phonet_attrs,
60700a2430fSAndrzej Pietrasiewicz .ct_owner = THIS_MODULE,
60800a2430fSAndrzej Pietrasiewicz };
60900a2430fSAndrzej Pietrasiewicz
phonet_free_inst(struct usb_function_instance * f)61000a2430fSAndrzej Pietrasiewicz static void phonet_free_inst(struct usb_function_instance *f)
61100a2430fSAndrzej Pietrasiewicz {
61200a2430fSAndrzej Pietrasiewicz struct f_phonet_opts *opts;
61300a2430fSAndrzej Pietrasiewicz
61400a2430fSAndrzej Pietrasiewicz opts = container_of(f, struct f_phonet_opts, func_inst);
61500a2430fSAndrzej Pietrasiewicz if (opts->bound)
61600a2430fSAndrzej Pietrasiewicz gphonet_cleanup(opts->net);
61700a2430fSAndrzej Pietrasiewicz else
61800a2430fSAndrzej Pietrasiewicz free_netdev(opts->net);
61900a2430fSAndrzej Pietrasiewicz kfree(opts);
62000a2430fSAndrzej Pietrasiewicz }
62100a2430fSAndrzej Pietrasiewicz
phonet_alloc_inst(void)62200a2430fSAndrzej Pietrasiewicz static struct usb_function_instance *phonet_alloc_inst(void)
62300a2430fSAndrzej Pietrasiewicz {
62400a2430fSAndrzej Pietrasiewicz struct f_phonet_opts *opts;
62500a2430fSAndrzej Pietrasiewicz
62600a2430fSAndrzej Pietrasiewicz opts = kzalloc(sizeof(*opts), GFP_KERNEL);
62700a2430fSAndrzej Pietrasiewicz if (!opts)
62800a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
62900a2430fSAndrzej Pietrasiewicz
63000a2430fSAndrzej Pietrasiewicz opts->func_inst.free_func_inst = phonet_free_inst;
63100a2430fSAndrzej Pietrasiewicz opts->net = gphonet_setup_default();
63200a2430fSAndrzej Pietrasiewicz if (IS_ERR(opts->net)) {
63300a2430fSAndrzej Pietrasiewicz struct net_device *net = opts->net;
63400a2430fSAndrzej Pietrasiewicz kfree(opts);
63500a2430fSAndrzej Pietrasiewicz return ERR_CAST(net);
63600a2430fSAndrzej Pietrasiewicz }
63700a2430fSAndrzej Pietrasiewicz
63800a2430fSAndrzej Pietrasiewicz config_group_init_type_name(&opts->func_inst.group, "",
63900a2430fSAndrzej Pietrasiewicz &phonet_func_type);
64000a2430fSAndrzej Pietrasiewicz
64100a2430fSAndrzej Pietrasiewicz return &opts->func_inst;
64200a2430fSAndrzej Pietrasiewicz }
64300a2430fSAndrzej Pietrasiewicz
phonet_free(struct usb_function * f)64400a2430fSAndrzej Pietrasiewicz static void phonet_free(struct usb_function *f)
64500a2430fSAndrzej Pietrasiewicz {
64600a2430fSAndrzej Pietrasiewicz struct f_phonet *phonet;
64700a2430fSAndrzej Pietrasiewicz
64800a2430fSAndrzej Pietrasiewicz phonet = func_to_pn(f);
64900a2430fSAndrzej Pietrasiewicz kfree(phonet);
65000a2430fSAndrzej Pietrasiewicz }
65100a2430fSAndrzej Pietrasiewicz
pn_unbind(struct usb_configuration * c,struct usb_function * f)65200a2430fSAndrzej Pietrasiewicz static void pn_unbind(struct usb_configuration *c, struct usb_function *f)
65300a2430fSAndrzej Pietrasiewicz {
65400a2430fSAndrzej Pietrasiewicz struct f_phonet *fp = func_to_pn(f);
65500a2430fSAndrzej Pietrasiewicz int i;
65600a2430fSAndrzej Pietrasiewicz
65700a2430fSAndrzej Pietrasiewicz /* We are already disconnected */
65800a2430fSAndrzej Pietrasiewicz if (fp->in_req)
65900a2430fSAndrzej Pietrasiewicz usb_ep_free_request(fp->in_ep, fp->in_req);
66000a2430fSAndrzej Pietrasiewicz for (i = 0; i < phonet_rxq_size; i++)
66100a2430fSAndrzej Pietrasiewicz if (fp->out_reqv[i])
66200a2430fSAndrzej Pietrasiewicz usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
66300a2430fSAndrzej Pietrasiewicz
66400a2430fSAndrzej Pietrasiewicz usb_free_all_descriptors(f);
66500a2430fSAndrzej Pietrasiewicz }
66600a2430fSAndrzej Pietrasiewicz
phonet_alloc(struct usb_function_instance * fi)66700a2430fSAndrzej Pietrasiewicz static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
66800a2430fSAndrzej Pietrasiewicz {
66900a2430fSAndrzej Pietrasiewicz struct f_phonet *fp;
67000a2430fSAndrzej Pietrasiewicz struct f_phonet_opts *opts;
67100a2430fSAndrzej Pietrasiewicz
672*88476b26SGustavo A. R. Silva fp = kzalloc(struct_size(fp, out_reqv, phonet_rxq_size), GFP_KERNEL);
67300a2430fSAndrzej Pietrasiewicz if (!fp)
67400a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
67500a2430fSAndrzej Pietrasiewicz
67600a2430fSAndrzej Pietrasiewicz opts = container_of(fi, struct f_phonet_opts, func_inst);
67700a2430fSAndrzej Pietrasiewicz
67800a2430fSAndrzej Pietrasiewicz fp->dev = opts->net;
67900a2430fSAndrzej Pietrasiewicz fp->function.name = "phonet";
68000a2430fSAndrzej Pietrasiewicz fp->function.bind = pn_bind;
68100a2430fSAndrzej Pietrasiewicz fp->function.unbind = pn_unbind;
68200a2430fSAndrzej Pietrasiewicz fp->function.set_alt = pn_set_alt;
68300a2430fSAndrzej Pietrasiewicz fp->function.get_alt = pn_get_alt;
68400a2430fSAndrzej Pietrasiewicz fp->function.disable = pn_disconnect;
68500a2430fSAndrzej Pietrasiewicz fp->function.free_func = phonet_free;
68600a2430fSAndrzej Pietrasiewicz spin_lock_init(&fp->rx.lock);
68700a2430fSAndrzej Pietrasiewicz
68800a2430fSAndrzej Pietrasiewicz return &fp->function;
68900a2430fSAndrzej Pietrasiewicz }
69000a2430fSAndrzej Pietrasiewicz
gphonet_setup_default(void)69100a2430fSAndrzej Pietrasiewicz struct net_device *gphonet_setup_default(void)
69200a2430fSAndrzej Pietrasiewicz {
69300a2430fSAndrzej Pietrasiewicz struct net_device *dev;
69400a2430fSAndrzej Pietrasiewicz struct phonet_port *port;
69500a2430fSAndrzej Pietrasiewicz
69600a2430fSAndrzej Pietrasiewicz /* Create net device */
697ae045e24SLinus Torvalds dev = alloc_netdev(sizeof(*port), "upnlink%d", NET_NAME_UNKNOWN,
698ae045e24SLinus Torvalds pn_net_setup);
69900a2430fSAndrzej Pietrasiewicz if (!dev)
70000a2430fSAndrzej Pietrasiewicz return ERR_PTR(-ENOMEM);
70100a2430fSAndrzej Pietrasiewicz
70200a2430fSAndrzej Pietrasiewicz port = netdev_priv(dev);
70300a2430fSAndrzej Pietrasiewicz spin_lock_init(&port->lock);
70400a2430fSAndrzej Pietrasiewicz netif_carrier_off(dev);
70500a2430fSAndrzej Pietrasiewicz
70600a2430fSAndrzej Pietrasiewicz return dev;
70700a2430fSAndrzej Pietrasiewicz }
70800a2430fSAndrzej Pietrasiewicz
gphonet_set_gadget(struct net_device * net,struct usb_gadget * g)70900a2430fSAndrzej Pietrasiewicz void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
71000a2430fSAndrzej Pietrasiewicz {
71100a2430fSAndrzej Pietrasiewicz SET_NETDEV_DEV(net, &g->dev);
71200a2430fSAndrzej Pietrasiewicz }
71300a2430fSAndrzej Pietrasiewicz
gphonet_register_netdev(struct net_device * net)71400a2430fSAndrzej Pietrasiewicz int gphonet_register_netdev(struct net_device *net)
71500a2430fSAndrzej Pietrasiewicz {
71600a2430fSAndrzej Pietrasiewicz int status;
71700a2430fSAndrzej Pietrasiewicz
71800a2430fSAndrzej Pietrasiewicz status = register_netdev(net);
71900a2430fSAndrzej Pietrasiewicz if (status)
72000a2430fSAndrzej Pietrasiewicz free_netdev(net);
72100a2430fSAndrzej Pietrasiewicz
72200a2430fSAndrzej Pietrasiewicz return status;
72300a2430fSAndrzej Pietrasiewicz }
72400a2430fSAndrzej Pietrasiewicz
gphonet_cleanup(struct net_device * dev)72500a2430fSAndrzej Pietrasiewicz void gphonet_cleanup(struct net_device *dev)
72600a2430fSAndrzej Pietrasiewicz {
72700a2430fSAndrzej Pietrasiewicz unregister_netdev(dev);
72800a2430fSAndrzej Pietrasiewicz }
72900a2430fSAndrzej Pietrasiewicz
73000a2430fSAndrzej Pietrasiewicz DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
73100a2430fSAndrzej Pietrasiewicz MODULE_AUTHOR("Rémi Denis-Courmont");
73200a2430fSAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
733