brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · ad5121e Raw
434 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * phonet.c -- USB CDC Phonet host driver4 *5 * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved.6 *7 * Author: Rémi Denis-Courmont8 */9 10#include <linux/kernel.h>11#include <linux/mm.h>12#include <linux/module.h>13#include <linux/gfp.h>14#include <linux/usb.h>15#include <linux/usb/cdc.h>16#include <linux/netdevice.h>17#include <linux/if_arp.h>18#include <linux/if_phonet.h>19#include <linux/phonet.h>20 21#define PN_MEDIA_USB	0x1B22 23static const unsigned rxq_size = 17;24 25struct usbpn_dev {26	struct net_device	*dev;27 28	struct usb_interface	*intf, *data_intf;29	struct usb_device	*usb;30	unsigned int		tx_pipe, rx_pipe;31	u8 active_setting;32	u8 disconnected;33 34	unsigned		tx_queue;35	spinlock_t		tx_lock;36 37	spinlock_t		rx_lock;38	struct sk_buff		*rx_skb;39	struct urb		*urbs[];40};41 42static void tx_complete(struct urb *req);43static void rx_complete(struct urb *req);44 45/*46 * Network device callbacks47 */48static netdev_tx_t usbpn_xmit(struct sk_buff *skb, struct net_device *dev)49{50	struct usbpn_dev *pnd = netdev_priv(dev);51	struct urb *req = NULL;52	unsigned long flags;53	int err;54 55	if (skb->protocol != htons(ETH_P_PHONET))56		goto drop;57 58	req = usb_alloc_urb(0, GFP_ATOMIC);59	if (!req)60		goto drop;61	usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len,62				tx_complete, skb);63	req->transfer_flags = URB_ZERO_PACKET;64	err = usb_submit_urb(req, GFP_ATOMIC);65	if (err) {66		usb_free_urb(req);67		goto drop;68	}69 70	spin_lock_irqsave(&pnd->tx_lock, flags);71	pnd->tx_queue++;72	if (pnd->tx_queue >= dev->tx_queue_len)73		netif_stop_queue(dev);74	spin_unlock_irqrestore(&pnd->tx_lock, flags);75	return NETDEV_TX_OK;76 77drop:78	dev_kfree_skb(skb);79	dev->stats.tx_dropped++;80	return NETDEV_TX_OK;81}82 83static void tx_complete(struct urb *req)84{85	struct sk_buff *skb = req->context;86	struct net_device *dev = skb->dev;87	struct usbpn_dev *pnd = netdev_priv(dev);88	int status = req->status;89	unsigned long flags;90 91	switch (status) {92	case 0:93		dev->stats.tx_bytes += skb->len;94		break;95 96	case -ENOENT:97	case -ECONNRESET:98	case -ESHUTDOWN:99		dev->stats.tx_aborted_errors++;100		fallthrough;101	default:102		dev->stats.tx_errors++;103		dev_dbg(&dev->dev, "TX error (%d)\n", status);104	}105	dev->stats.tx_packets++;106 107	spin_lock_irqsave(&pnd->tx_lock, flags);108	pnd->tx_queue--;109	netif_wake_queue(dev);110	spin_unlock_irqrestore(&pnd->tx_lock, flags);111 112	dev_kfree_skb_any(skb);113	usb_free_urb(req);114}115 116static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)117{118	struct net_device *dev = pnd->dev;119	struct page *page;120	int err;121 122	page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC);123	if (!page)124		return -ENOMEM;125 126	usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page),127				PAGE_SIZE, rx_complete, dev);128	req->transfer_flags = 0;129	err = usb_submit_urb(req, gfp_flags);130	if (unlikely(err)) {131		dev_dbg(&dev->dev, "RX submit error (%d)\n", err);132		put_page(page);133	}134	return err;135}136 137static void rx_complete(struct urb *req)138{139	struct net_device *dev = req->context;140	struct usbpn_dev *pnd = netdev_priv(dev);141	struct page *page = virt_to_page(req->transfer_buffer);142	struct sk_buff *skb;143	unsigned long flags;144	int status = req->status;145 146	switch (status) {147	case 0:148		spin_lock_irqsave(&pnd->rx_lock, flags);149		skb = pnd->rx_skb;150		if (!skb) {151			skb = pnd->rx_skb = netdev_alloc_skb(dev, 12);152			if (likely(skb)) {153				/* Can't use pskb_pull() on page in IRQ */154				skb_put_data(skb, page_address(page), 1);155				skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,156						page, 1, req->actual_length,157						PAGE_SIZE);158				page = NULL;159			}160		} else {161			skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,162					page, 0, req->actual_length,163					PAGE_SIZE);164			page = NULL;165		}166		if (req->actual_length < PAGE_SIZE)167			pnd->rx_skb = NULL; /* Last fragment */168		else169			skb = NULL;170		spin_unlock_irqrestore(&pnd->rx_lock, flags);171		if (skb) {172			skb->protocol = htons(ETH_P_PHONET);173			skb_reset_mac_header(skb);174			__skb_pull(skb, 1);175			skb->dev = dev;176			dev->stats.rx_packets++;177			dev->stats.rx_bytes += skb->len;178 179			netif_rx(skb);180		}181		goto resubmit;182 183	case -ENOENT:184	case -ECONNRESET:185	case -ESHUTDOWN:186		req = NULL;187		break;188 189	case -EOVERFLOW:190		dev->stats.rx_over_errors++;191		dev_dbg(&dev->dev, "RX overflow\n");192		break;193 194	case -EILSEQ:195		dev->stats.rx_crc_errors++;196		break;197	}198 199	dev->stats.rx_errors++;200resubmit:201	if (page)202		put_page(page);203	if (req)204		rx_submit(pnd, req, GFP_ATOMIC);205}206 207static int usbpn_close(struct net_device *dev);208 209static int usbpn_open(struct net_device *dev)210{211	struct usbpn_dev *pnd = netdev_priv(dev);212	int err;213	unsigned i;214	unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber;215 216	err = usb_set_interface(pnd->usb, num, pnd->active_setting);217	if (err)218		return err;219 220	for (i = 0; i < rxq_size; i++) {221		struct urb *req = usb_alloc_urb(0, GFP_KERNEL);222 223		if (!req || rx_submit(pnd, req, GFP_KERNEL)) {224			usb_free_urb(req);225			usbpn_close(dev);226			return -ENOMEM;227		}228		pnd->urbs[i] = req;229	}230 231	netif_wake_queue(dev);232	return 0;233}234 235static int usbpn_close(struct net_device *dev)236{237	struct usbpn_dev *pnd = netdev_priv(dev);238	unsigned i;239	unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber;240 241	netif_stop_queue(dev);242 243	for (i = 0; i < rxq_size; i++) {244		struct urb *req = pnd->urbs[i];245 246		if (!req)247			continue;248		usb_kill_urb(req);249		usb_free_urb(req);250		pnd->urbs[i] = NULL;251	}252 253	return usb_set_interface(pnd->usb, num, !pnd->active_setting);254}255 256static int usbpn_siocdevprivate(struct net_device *dev, struct ifreq *ifr,257				void __user *data, int cmd)258{259	struct if_phonet_req *req = (struct if_phonet_req *)ifr;260 261	switch (cmd) {262	case SIOCPNGAUTOCONF:263		req->ifr_phonet_autoconf.device = PN_DEV_PC;264		return 0;265	}266	return -ENOIOCTLCMD;267}268 269static const struct net_device_ops usbpn_ops = {270	.ndo_open	= usbpn_open,271	.ndo_stop	= usbpn_close,272	.ndo_start_xmit = usbpn_xmit,273	.ndo_siocdevprivate = usbpn_siocdevprivate,274};275 276static void usbpn_setup(struct net_device *dev)277{278	const u8 addr = PN_MEDIA_USB;279 280	dev->features		= 0;281	dev->netdev_ops		= &usbpn_ops;282	dev->header_ops		= &phonet_header_ops;283	dev->type		= ARPHRD_PHONET;284	dev->flags		= IFF_POINTOPOINT | IFF_NOARP;285	dev->mtu		= PHONET_MAX_MTU;286	dev->min_mtu		= PHONET_MIN_MTU;287	dev->max_mtu		= PHONET_MAX_MTU;288	dev->hard_header_len	= 1;289	dev->addr_len		= 1;290	dev_addr_set(dev, &addr);291	dev->tx_queue_len	= 3;292 293	dev->needs_free_netdev	= true;294}295 296/*297 * USB driver callbacks298 */299static const struct usb_device_id usbpn_ids[] = {300	{301		.match_flags = USB_DEVICE_ID_MATCH_VENDOR302			| USB_DEVICE_ID_MATCH_INT_CLASS303			| USB_DEVICE_ID_MATCH_INT_SUBCLASS,304		.idVendor = 0x0421, /* Nokia */305		.bInterfaceClass = USB_CLASS_COMM,306		.bInterfaceSubClass = 0xFE,307	},308	{ },309};310 311MODULE_DEVICE_TABLE(usb, usbpn_ids);312 313static struct usb_driver usbpn_driver;314 315static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)316{317	static const char ifname[] = "usbpn%d";318	const struct usb_cdc_union_desc *union_header = NULL;319	const struct usb_host_interface *data_desc;320	struct usb_interface *data_intf;321	struct usb_device *usbdev = interface_to_usbdev(intf);322	struct net_device *dev;323	struct usbpn_dev *pnd;324	u8 *data;325	int phonet = 0;326	int len, err;327	struct usb_cdc_parsed_header hdr;328 329	data = intf->altsetting->extra;330	len = intf->altsetting->extralen;331	cdc_parse_cdc_header(&hdr, intf, data, len);332	union_header = hdr.usb_cdc_union_desc;333	phonet = hdr.phonet_magic_present;334 335	if (!union_header || !phonet)336		return -EINVAL;337 338	data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0);339	if (data_intf == NULL)340		return -ENODEV;341	/* Data interface has one inactive and one active setting */342	if (data_intf->num_altsetting != 2)343		return -EINVAL;344	if (data_intf->altsetting[0].desc.bNumEndpoints == 0 &&345	    data_intf->altsetting[1].desc.bNumEndpoints == 2)346		data_desc = data_intf->altsetting + 1;347	else348	if (data_intf->altsetting[0].desc.bNumEndpoints == 2 &&349	    data_intf->altsetting[1].desc.bNumEndpoints == 0)350		data_desc = data_intf->altsetting;351	else352		return -EINVAL;353 354	dev = alloc_netdev(struct_size(pnd, urbs, rxq_size), ifname,355			   NET_NAME_UNKNOWN, usbpn_setup);356	if (!dev)357		return -ENOMEM;358 359	pnd = netdev_priv(dev);360	SET_NETDEV_DEV(dev, &intf->dev);361 362	pnd->dev = dev;363	pnd->usb = usbdev;364	pnd->intf = intf;365	pnd->data_intf = data_intf;366	spin_lock_init(&pnd->tx_lock);367	spin_lock_init(&pnd->rx_lock);368	/* Endpoints */369	if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) {370		pnd->rx_pipe = usb_rcvbulkpipe(usbdev,371			data_desc->endpoint[0].desc.bEndpointAddress);372		pnd->tx_pipe = usb_sndbulkpipe(usbdev,373			data_desc->endpoint[1].desc.bEndpointAddress);374	} else {375		pnd->rx_pipe = usb_rcvbulkpipe(usbdev,376			data_desc->endpoint[1].desc.bEndpointAddress);377		pnd->tx_pipe = usb_sndbulkpipe(usbdev,378			data_desc->endpoint[0].desc.bEndpointAddress);379	}380	pnd->active_setting = data_desc - data_intf->altsetting;381 382	err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd);383	if (err)384		goto out;385 386	/* Force inactive mode until the network device is brought UP */387	usb_set_interface(usbdev, union_header->bSlaveInterface0,388				!pnd->active_setting);389	usb_set_intfdata(intf, pnd);390 391	err = register_netdev(dev);392	if (err) {393		/* Set disconnected flag so that disconnect() returns early. */394		pnd->disconnected = 1;395		usb_driver_release_interface(&usbpn_driver, data_intf);396		goto out;397	}398 399	dev_dbg(&dev->dev, "USB CDC Phonet device found\n");400	return 0;401 402out:403	usb_set_intfdata(intf, NULL);404	free_netdev(dev);405	return err;406}407 408static void usbpn_disconnect(struct usb_interface *intf)409{410	struct usbpn_dev *pnd = usb_get_intfdata(intf);411 412	if (pnd->disconnected)413		return;414 415	pnd->disconnected = 1;416	usb_driver_release_interface(&usbpn_driver,417			(pnd->intf == intf) ? pnd->data_intf : pnd->intf);418	unregister_netdev(pnd->dev);419}420 421static struct usb_driver usbpn_driver = {422	.name =		"cdc_phonet",423	.probe =	usbpn_probe,424	.disconnect =	usbpn_disconnect,425	.id_table =	usbpn_ids,426	.disable_hub_initiated_lpm = 1,427};428 429module_usb_driver(usbpn_driver);430 431MODULE_AUTHOR("Remi Denis-Courmont");432MODULE_DESCRIPTION("USB CDC Phonet host interface");433MODULE_LICENSE("GPL");434