18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * CAIF USB handler
48c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2011
58c2ecf20Sopenharmony_ci * Author:	Sjur Brendeland
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/mii.h>
148c2ecf20Sopenharmony_ci#include <linux/usb.h>
158c2ecf20Sopenharmony_ci#include <linux/usb/usbnet.h>
168c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
178c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
188c2ecf20Sopenharmony_ci#include <net/caif/caif_dev.h>
198c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h>
208c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h>
218c2ecf20Sopenharmony_ci#include <net/caif/cfcnfg.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define CFUSB_PAD_DESCR_SZ 1	/* Alignment descriptor length */
268c2ecf20Sopenharmony_ci#define CFUSB_ALIGNMENT 4	/* Number of bytes to align. */
278c2ecf20Sopenharmony_ci#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1)
288c2ecf20Sopenharmony_ci#define STE_USB_VID 0x04cc	/* USB Product ID for ST-Ericsson */
298c2ecf20Sopenharmony_ci#define STE_USB_PID_CAIF 0x230f	/* Product id for CAIF Modems */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct cfusbl {
328c2ecf20Sopenharmony_ci	struct cflayer layer;
338c2ecf20Sopenharmony_ci	u8 tx_eth_hdr[ETH_HLEN];
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic bool pack_added;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	u8 hpad;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	/* Remove padding. */
438c2ecf20Sopenharmony_ci	cfpkt_extr_head(pkt, &hpad, 1);
448c2ecf20Sopenharmony_ci	cfpkt_extr_head(pkt, NULL, hpad);
458c2ecf20Sopenharmony_ci	return layr->up->receive(layr->up, pkt);
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct caif_payload_info *info;
518c2ecf20Sopenharmony_ci	u8 hpad;
528c2ecf20Sopenharmony_ci	u8 zeros[CFUSB_ALIGNMENT];
538c2ecf20Sopenharmony_ci	struct sk_buff *skb;
548c2ecf20Sopenharmony_ci	struct cfusbl *usbl = container_of(layr, struct cfusbl, layer);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	skb = cfpkt_tonative(pkt);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
598c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	info = cfpkt_info(pkt);
628c2ecf20Sopenharmony_ci	hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) {
658c2ecf20Sopenharmony_ci		pr_warn("Headroom too small\n");
668c2ecf20Sopenharmony_ci		kfree_skb(skb);
678c2ecf20Sopenharmony_ci		return -EIO;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci	memset(zeros, 0, hpad);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	cfpkt_add_head(pkt, zeros, hpad);
728c2ecf20Sopenharmony_ci	cfpkt_add_head(pkt, &hpad, 1);
738c2ecf20Sopenharmony_ci	cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr));
748c2ecf20Sopenharmony_ci	return layr->dn->transmit(layr->dn, pkt);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
788c2ecf20Sopenharmony_ci			   int phyid)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	if (layr->up && layr->up->ctrlcmd)
818c2ecf20Sopenharmony_ci		layr->up->ctrlcmd(layr->up, ctrl, layr->id);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic struct cflayer *cfusbl_create(int phyid, u8 ethaddr[ETH_ALEN],
858c2ecf20Sopenharmony_ci				      u8 braddr[ETH_ALEN])
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!this)
908c2ecf20Sopenharmony_ci		return NULL;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	caif_assert(offsetof(struct cfusbl, layer) == 0);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	memset(&this->layer, 0, sizeof(this->layer));
958c2ecf20Sopenharmony_ci	this->layer.receive = cfusbl_receive;
968c2ecf20Sopenharmony_ci	this->layer.transmit = cfusbl_transmit;
978c2ecf20Sopenharmony_ci	this->layer.ctrlcmd = cfusbl_ctrlcmd;
988c2ecf20Sopenharmony_ci	snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid);
998c2ecf20Sopenharmony_ci	this->layer.id = phyid;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * Construct TX ethernet header:
1038c2ecf20Sopenharmony_ci	 *	0-5	destination address
1048c2ecf20Sopenharmony_ci	 *	5-11	source address
1058c2ecf20Sopenharmony_ci	 *	12-13	protocol type
1068c2ecf20Sopenharmony_ci	 */
1078c2ecf20Sopenharmony_ci	ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr);
1088c2ecf20Sopenharmony_ci	ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr);
1098c2ecf20Sopenharmony_ci	this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff;
1108c2ecf20Sopenharmony_ci	this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff;
1118c2ecf20Sopenharmony_ci	pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n",
1128c2ecf20Sopenharmony_ci			this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN,
1138c2ecf20Sopenharmony_ci			this->tx_eth_hdr[12], this->tx_eth_hdr[13]);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return (struct cflayer *) this;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void cfusbl_release(struct cflayer *layer)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	kfree(layer);
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic struct packet_type caif_usb_type __read_mostly = {
1248c2ecf20Sopenharmony_ci	.type = cpu_to_be16(ETH_P_802_EX1),
1258c2ecf20Sopenharmony_ci};
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
1288c2ecf20Sopenharmony_ci				void *ptr)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1318c2ecf20Sopenharmony_ci	struct caif_dev_common common;
1328c2ecf20Sopenharmony_ci	struct cflayer *layer, *link_support;
1338c2ecf20Sopenharmony_ci	struct usbnet *usbnet;
1348c2ecf20Sopenharmony_ci	struct usb_device *usbdev;
1358c2ecf20Sopenharmony_ci	int res;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED)
1388c2ecf20Sopenharmony_ci		return 0;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Check whether we have a NCM device, and find its VID/PID. */
1418c2ecf20Sopenharmony_ci	if (!(dev->dev.parent && dev->dev.parent->driver &&
1428c2ecf20Sopenharmony_ci	      strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0))
1438c2ecf20Sopenharmony_ci		return 0;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	usbnet = netdev_priv(dev);
1468c2ecf20Sopenharmony_ci	usbdev = usbnet->udev;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n",
1498c2ecf20Sopenharmony_ci		le16_to_cpu(usbdev->descriptor.idVendor),
1508c2ecf20Sopenharmony_ci		le16_to_cpu(usbdev->descriptor.idProduct));
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* Check for VID/PID that supports CAIF */
1538c2ecf20Sopenharmony_ci	if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID &&
1548c2ecf20Sopenharmony_ci		le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF))
1558c2ecf20Sopenharmony_ci		return 0;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (what == NETDEV_UNREGISTER)
1588c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (what != NETDEV_REGISTER)
1618c2ecf20Sopenharmony_ci		return 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	__module_get(THIS_MODULE);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	memset(&common, 0, sizeof(common));
1668c2ecf20Sopenharmony_ci	common.use_frag = false;
1678c2ecf20Sopenharmony_ci	common.use_fcs = false;
1688c2ecf20Sopenharmony_ci	common.use_stx = false;
1698c2ecf20Sopenharmony_ci	common.link_select = CAIF_LINK_HIGH_BANDW;
1708c2ecf20Sopenharmony_ci	common.flowctrl = NULL;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	link_support = cfusbl_create(dev->ifindex, dev->dev_addr,
1738c2ecf20Sopenharmony_ci					dev->broadcast);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (!link_support)
1768c2ecf20Sopenharmony_ci		return -ENOMEM;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (dev->num_tx_queues > 1)
1798c2ecf20Sopenharmony_ci		pr_warn("USB device uses more than one tx queue\n");
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	res = caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN,
1828c2ecf20Sopenharmony_ci			&layer, &caif_usb_type.func);
1838c2ecf20Sopenharmony_ci	if (res)
1848c2ecf20Sopenharmony_ci		goto err;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (!pack_added)
1878c2ecf20Sopenharmony_ci		dev_add_pack(&caif_usb_type);
1888c2ecf20Sopenharmony_ci	pack_added = true;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	strlcpy(layer->name, dev->name, sizeof(layer->name));
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_cierr:
1948c2ecf20Sopenharmony_ci	cfusbl_release(link_support);
1958c2ecf20Sopenharmony_ci	return res;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic struct notifier_block caif_device_notifier = {
1998c2ecf20Sopenharmony_ci	.notifier_call = cfusbl_device_notify,
2008c2ecf20Sopenharmony_ci	.priority = 0,
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int __init cfusbl_init(void)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	return register_netdevice_notifier(&caif_device_notifier);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic void __exit cfusbl_exit(void)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&caif_device_notifier);
2118c2ecf20Sopenharmony_ci	dev_remove_pack(&caif_usb_type);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cimodule_init(cfusbl_init);
2158c2ecf20Sopenharmony_cimodule_exit(cfusbl_exit);
216