xref: /kernel/linux/linux-6.6/net/caif/caif_usb.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CAIF USB handler
462306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2011
562306a36Sopenharmony_ci * Author:	Sjur Brendeland
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/netdevice.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/mii.h>
1462306a36Sopenharmony_ci#include <linux/usb.h>
1562306a36Sopenharmony_ci#include <linux/usb/usbnet.h>
1662306a36Sopenharmony_ci#include <linux/etherdevice.h>
1762306a36Sopenharmony_ci#include <net/netns/generic.h>
1862306a36Sopenharmony_ci#include <net/caif/caif_dev.h>
1962306a36Sopenharmony_ci#include <net/caif/caif_layer.h>
2062306a36Sopenharmony_ci#include <net/caif/cfpkt.h>
2162306a36Sopenharmony_ci#include <net/caif/cfcnfg.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define CFUSB_PAD_DESCR_SZ 1	/* Alignment descriptor length */
2662306a36Sopenharmony_ci#define CFUSB_ALIGNMENT 4	/* Number of bytes to align. */
2762306a36Sopenharmony_ci#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1)
2862306a36Sopenharmony_ci#define STE_USB_VID 0x04cc	/* USB Product ID for ST-Ericsson */
2962306a36Sopenharmony_ci#define STE_USB_PID_CAIF 0x230f	/* Product id for CAIF Modems */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct cfusbl {
3262306a36Sopenharmony_ci	struct cflayer layer;
3362306a36Sopenharmony_ci	u8 tx_eth_hdr[ETH_HLEN];
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic bool pack_added;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	u8 hpad;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	/* Remove padding. */
4362306a36Sopenharmony_ci	cfpkt_extr_head(pkt, &hpad, 1);
4462306a36Sopenharmony_ci	cfpkt_extr_head(pkt, NULL, hpad);
4562306a36Sopenharmony_ci	return layr->up->receive(layr->up, pkt);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct caif_payload_info *info;
5162306a36Sopenharmony_ci	u8 hpad;
5262306a36Sopenharmony_ci	u8 zeros[CFUSB_ALIGNMENT];
5362306a36Sopenharmony_ci	struct sk_buff *skb;
5462306a36Sopenharmony_ci	struct cfusbl *usbl = container_of(layr, struct cfusbl, layer);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	skb = cfpkt_tonative(pkt);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	skb_reset_network_header(skb);
5962306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_IP);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	info = cfpkt_info(pkt);
6262306a36Sopenharmony_ci	hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) {
6562306a36Sopenharmony_ci		pr_warn("Headroom too small\n");
6662306a36Sopenharmony_ci		kfree_skb(skb);
6762306a36Sopenharmony_ci		return -EIO;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	memset(zeros, 0, hpad);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	cfpkt_add_head(pkt, zeros, hpad);
7262306a36Sopenharmony_ci	cfpkt_add_head(pkt, &hpad, 1);
7362306a36Sopenharmony_ci	cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr));
7462306a36Sopenharmony_ci	return layr->dn->transmit(layr->dn, pkt);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
7862306a36Sopenharmony_ci			   int phyid)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	if (layr->up && layr->up->ctrlcmd)
8162306a36Sopenharmony_ci		layr->up->ctrlcmd(layr->up, ctrl, layr->id);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic struct cflayer *cfusbl_create(int phyid, const u8 ethaddr[ETH_ALEN],
8562306a36Sopenharmony_ci				      u8 braddr[ETH_ALEN])
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!this)
9062306a36Sopenharmony_ci		return NULL;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	caif_assert(offsetof(struct cfusbl, layer) == 0);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	memset(&this->layer, 0, sizeof(this->layer));
9562306a36Sopenharmony_ci	this->layer.receive = cfusbl_receive;
9662306a36Sopenharmony_ci	this->layer.transmit = cfusbl_transmit;
9762306a36Sopenharmony_ci	this->layer.ctrlcmd = cfusbl_ctrlcmd;
9862306a36Sopenharmony_ci	snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid);
9962306a36Sopenharmony_ci	this->layer.id = phyid;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/*
10262306a36Sopenharmony_ci	 * Construct TX ethernet header:
10362306a36Sopenharmony_ci	 *	0-5	destination address
10462306a36Sopenharmony_ci	 *	5-11	source address
10562306a36Sopenharmony_ci	 *	12-13	protocol type
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr);
10862306a36Sopenharmony_ci	ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr);
10962306a36Sopenharmony_ci	this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff;
11062306a36Sopenharmony_ci	this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff;
11162306a36Sopenharmony_ci	pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n",
11262306a36Sopenharmony_ci			this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN,
11362306a36Sopenharmony_ci			this->tx_eth_hdr[12], this->tx_eth_hdr[13]);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return (struct cflayer *) this;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void cfusbl_release(struct cflayer *layer)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	kfree(layer);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic struct packet_type caif_usb_type __read_mostly = {
12462306a36Sopenharmony_ci	.type = cpu_to_be16(ETH_P_802_EX1),
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
12862306a36Sopenharmony_ci				void *ptr)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
13162306a36Sopenharmony_ci	struct caif_dev_common common;
13262306a36Sopenharmony_ci	struct cflayer *layer, *link_support;
13362306a36Sopenharmony_ci	struct usbnet *usbnet;
13462306a36Sopenharmony_ci	struct usb_device *usbdev;
13562306a36Sopenharmony_ci	int res;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED)
13862306a36Sopenharmony_ci		return 0;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Check whether we have a NCM device, and find its VID/PID. */
14162306a36Sopenharmony_ci	if (!(dev->dev.parent && dev->dev.parent->driver &&
14262306a36Sopenharmony_ci	      strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0))
14362306a36Sopenharmony_ci		return 0;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	usbnet = netdev_priv(dev);
14662306a36Sopenharmony_ci	usbdev = usbnet->udev;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n",
14962306a36Sopenharmony_ci		le16_to_cpu(usbdev->descriptor.idVendor),
15062306a36Sopenharmony_ci		le16_to_cpu(usbdev->descriptor.idProduct));
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* Check for VID/PID that supports CAIF */
15362306a36Sopenharmony_ci	if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID &&
15462306a36Sopenharmony_ci		le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF))
15562306a36Sopenharmony_ci		return 0;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (what == NETDEV_UNREGISTER)
15862306a36Sopenharmony_ci		module_put(THIS_MODULE);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (what != NETDEV_REGISTER)
16162306a36Sopenharmony_ci		return 0;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	__module_get(THIS_MODULE);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	memset(&common, 0, sizeof(common));
16662306a36Sopenharmony_ci	common.use_frag = false;
16762306a36Sopenharmony_ci	common.use_fcs = false;
16862306a36Sopenharmony_ci	common.use_stx = false;
16962306a36Sopenharmony_ci	common.link_select = CAIF_LINK_HIGH_BANDW;
17062306a36Sopenharmony_ci	common.flowctrl = NULL;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	link_support = cfusbl_create(dev->ifindex, dev->dev_addr,
17362306a36Sopenharmony_ci					dev->broadcast);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (!link_support)
17662306a36Sopenharmony_ci		return -ENOMEM;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (dev->num_tx_queues > 1)
17962306a36Sopenharmony_ci		pr_warn("USB device uses more than one tx queue\n");
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	res = caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN,
18262306a36Sopenharmony_ci			&layer, &caif_usb_type.func);
18362306a36Sopenharmony_ci	if (res)
18462306a36Sopenharmony_ci		goto err;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (!pack_added)
18762306a36Sopenharmony_ci		dev_add_pack(&caif_usb_type);
18862306a36Sopenharmony_ci	pack_added = true;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	strscpy(layer->name, dev->name, sizeof(layer->name));
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 0;
19362306a36Sopenharmony_cierr:
19462306a36Sopenharmony_ci	cfusbl_release(link_support);
19562306a36Sopenharmony_ci	return res;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic struct notifier_block caif_device_notifier = {
19962306a36Sopenharmony_ci	.notifier_call = cfusbl_device_notify,
20062306a36Sopenharmony_ci	.priority = 0,
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int __init cfusbl_init(void)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	return register_netdevice_notifier(&caif_device_notifier);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void __exit cfusbl_exit(void)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	unregister_netdevice_notifier(&caif_device_notifier);
21162306a36Sopenharmony_ci	dev_remove_pack(&caif_usb_type);
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_cimodule_init(cfusbl_init);
21562306a36Sopenharmony_cimodule_exit(cfusbl_exit);
216