18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * CAIF Interface registration.
48c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010
58c2ecf20Sopenharmony_ci * Author:	Sjur Brendeland
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont
88c2ecf20Sopenharmony_ci *  and Sakari Ailus <sakari.ailus@nokia.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/if_arp.h>
158c2ecf20Sopenharmony_ci#include <linux/net.h>
168c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
208c2ecf20Sopenharmony_ci#include <net/netns/generic.h>
218c2ecf20Sopenharmony_ci#include <net/net_namespace.h>
228c2ecf20Sopenharmony_ci#include <net/pkt_sched.h>
238c2ecf20Sopenharmony_ci#include <net/caif/caif_device.h>
248c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h>
258c2ecf20Sopenharmony_ci#include <net/caif/caif_dev.h>
268c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h>
278c2ecf20Sopenharmony_ci#include <net/caif/cfcnfg.h>
288c2ecf20Sopenharmony_ci#include <net/caif/cfserl.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Used for local tracking of the CAIF net devices */
338c2ecf20Sopenharmony_cistruct caif_device_entry {
348c2ecf20Sopenharmony_ci	struct cflayer layer;
358c2ecf20Sopenharmony_ci	struct list_head list;
368c2ecf20Sopenharmony_ci	struct net_device *netdev;
378c2ecf20Sopenharmony_ci	int __percpu *pcpu_refcnt;
388c2ecf20Sopenharmony_ci	spinlock_t flow_lock;
398c2ecf20Sopenharmony_ci	struct sk_buff *xoff_skb;
408c2ecf20Sopenharmony_ci	void (*xoff_skb_dtor)(struct sk_buff *skb);
418c2ecf20Sopenharmony_ci	bool xoff;
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct caif_device_entry_list {
458c2ecf20Sopenharmony_ci	struct list_head list;
468c2ecf20Sopenharmony_ci	/* Protects simulanous deletes in list */
478c2ecf20Sopenharmony_ci	struct mutex lock;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct caif_net {
518c2ecf20Sopenharmony_ci	struct cfcnfg *cfg;
528c2ecf20Sopenharmony_ci	struct caif_device_entry_list caifdevs;
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic unsigned int caif_net_id;
568c2ecf20Sopenharmony_cistatic int q_high = 50; /* Percent */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistruct cfcnfg *get_cfcnfg(struct net *net)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct caif_net *caifn;
618c2ecf20Sopenharmony_ci	caifn = net_generic(net, caif_net_id);
628c2ecf20Sopenharmony_ci	return caifn->cfg;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(get_cfcnfg);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct caif_device_entry_list *caif_device_list(struct net *net)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct caif_net *caifn;
698c2ecf20Sopenharmony_ci	caifn = net_generic(net, caif_net_id);
708c2ecf20Sopenharmony_ci	return &caifn->caifdevs;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic void caifd_put(struct caif_device_entry *e)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	this_cpu_dec(*e->pcpu_refcnt);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic void caifd_hold(struct caif_device_entry *e)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	this_cpu_inc(*e->pcpu_refcnt);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic int caifd_refcnt_read(struct caif_device_entry *e)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	int i, refcnt = 0;
868c2ecf20Sopenharmony_ci	for_each_possible_cpu(i)
878c2ecf20Sopenharmony_ci		refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
888c2ecf20Sopenharmony_ci	return refcnt;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/* Allocate new CAIF device. */
928c2ecf20Sopenharmony_cistatic struct caif_device_entry *caif_device_alloc(struct net_device *dev)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
978c2ecf20Sopenharmony_ci	if (!caifd)
988c2ecf20Sopenharmony_ci		return NULL;
998c2ecf20Sopenharmony_ci	caifd->pcpu_refcnt = alloc_percpu(int);
1008c2ecf20Sopenharmony_ci	if (!caifd->pcpu_refcnt) {
1018c2ecf20Sopenharmony_ci		kfree(caifd);
1028c2ecf20Sopenharmony_ci		return NULL;
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci	caifd->netdev = dev;
1058c2ecf20Sopenharmony_ci	dev_hold(dev);
1068c2ecf20Sopenharmony_ci	return caifd;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic struct caif_device_entry *caif_get(struct net_device *dev)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct caif_device_entry_list *caifdevs =
1128c2ecf20Sopenharmony_ci	    caif_device_list(dev_net(dev));
1138c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	list_for_each_entry_rcu(caifd, &caifdevs->list, list,
1168c2ecf20Sopenharmony_ci				lockdep_rtnl_is_held()) {
1178c2ecf20Sopenharmony_ci		if (caifd->netdev == dev)
1188c2ecf20Sopenharmony_ci			return caifd;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci	return NULL;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic void caif_flow_cb(struct sk_buff *skb)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd;
1268c2ecf20Sopenharmony_ci	void (*dtor)(struct sk_buff *skb) = NULL;
1278c2ecf20Sopenharmony_ci	bool send_xoff;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	WARN_ON(skb->dev == NULL);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	rcu_read_lock();
1328c2ecf20Sopenharmony_ci	caifd = caif_get(skb->dev);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	WARN_ON(caifd == NULL);
1358c2ecf20Sopenharmony_ci	if (!caifd) {
1368c2ecf20Sopenharmony_ci		rcu_read_unlock();
1378c2ecf20Sopenharmony_ci		return;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	caifd_hold(caifd);
1418c2ecf20Sopenharmony_ci	rcu_read_unlock();
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	spin_lock_bh(&caifd->flow_lock);
1448c2ecf20Sopenharmony_ci	send_xoff = caifd->xoff;
1458c2ecf20Sopenharmony_ci	caifd->xoff = false;
1468c2ecf20Sopenharmony_ci	dtor = caifd->xoff_skb_dtor;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (WARN_ON(caifd->xoff_skb != skb))
1498c2ecf20Sopenharmony_ci		skb = NULL;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	caifd->xoff_skb = NULL;
1528c2ecf20Sopenharmony_ci	caifd->xoff_skb_dtor = NULL;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	spin_unlock_bh(&caifd->flow_lock);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (dtor && skb)
1578c2ecf20Sopenharmony_ci		dtor(skb);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (send_xoff)
1608c2ecf20Sopenharmony_ci		caifd->layer.up->
1618c2ecf20Sopenharmony_ci			ctrlcmd(caifd->layer.up,
1628c2ecf20Sopenharmony_ci				_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
1638c2ecf20Sopenharmony_ci				caifd->layer.id);
1648c2ecf20Sopenharmony_ci	caifd_put(caifd);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int transmit(struct cflayer *layer, struct cfpkt *pkt)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	int err, high = 0, qlen = 0;
1708c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd =
1718c2ecf20Sopenharmony_ci	    container_of(layer, struct caif_device_entry, layer);
1728c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1738c2ecf20Sopenharmony_ci	struct netdev_queue *txq;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	rcu_read_lock_bh();
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	skb = cfpkt_tonative(pkt);
1788c2ecf20Sopenharmony_ci	skb->dev = caifd->netdev;
1798c2ecf20Sopenharmony_ci	skb_reset_network_header(skb);
1808c2ecf20Sopenharmony_ci	skb->protocol = htons(ETH_P_CAIF);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	/* Check if we need to handle xoff */
1838c2ecf20Sopenharmony_ci	if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE))
1848c2ecf20Sopenharmony_ci		goto noxoff;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (unlikely(caifd->xoff))
1878c2ecf20Sopenharmony_ci		goto noxoff;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (likely(!netif_queue_stopped(caifd->netdev))) {
1908c2ecf20Sopenharmony_ci		struct Qdisc *sch;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		/* If we run with a TX queue, check if the queue is too long*/
1938c2ecf20Sopenharmony_ci		txq = netdev_get_tx_queue(skb->dev, 0);
1948c2ecf20Sopenharmony_ci		sch = rcu_dereference_bh(txq->qdisc);
1958c2ecf20Sopenharmony_ci		if (likely(qdisc_is_empty(sch)))
1968c2ecf20Sopenharmony_ci			goto noxoff;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		/* can check for explicit qdisc len value only !NOLOCK,
1998c2ecf20Sopenharmony_ci		 * always set flow off otherwise
2008c2ecf20Sopenharmony_ci		 */
2018c2ecf20Sopenharmony_ci		high = (caifd->netdev->tx_queue_len * q_high) / 100;
2028c2ecf20Sopenharmony_ci		if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high))
2038c2ecf20Sopenharmony_ci			goto noxoff;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	/* Hold lock while accessing xoff */
2078c2ecf20Sopenharmony_ci	spin_lock_bh(&caifd->flow_lock);
2088c2ecf20Sopenharmony_ci	if (caifd->xoff) {
2098c2ecf20Sopenharmony_ci		spin_unlock_bh(&caifd->flow_lock);
2108c2ecf20Sopenharmony_ci		goto noxoff;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/*
2148c2ecf20Sopenharmony_ci	 * Handle flow off, we do this by temporary hi-jacking this
2158c2ecf20Sopenharmony_ci	 * skb's destructor function, and replace it with our own
2168c2ecf20Sopenharmony_ci	 * flow-on callback. The callback will set flow-on and call
2178c2ecf20Sopenharmony_ci	 * the original destructor.
2188c2ecf20Sopenharmony_ci	 */
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
2218c2ecf20Sopenharmony_ci			netif_queue_stopped(caifd->netdev),
2228c2ecf20Sopenharmony_ci			qlen, high);
2238c2ecf20Sopenharmony_ci	caifd->xoff = true;
2248c2ecf20Sopenharmony_ci	caifd->xoff_skb = skb;
2258c2ecf20Sopenharmony_ci	caifd->xoff_skb_dtor = skb->destructor;
2268c2ecf20Sopenharmony_ci	skb->destructor = caif_flow_cb;
2278c2ecf20Sopenharmony_ci	spin_unlock_bh(&caifd->flow_lock);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	caifd->layer.up->ctrlcmd(caifd->layer.up,
2308c2ecf20Sopenharmony_ci					_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
2318c2ecf20Sopenharmony_ci					caifd->layer.id);
2328c2ecf20Sopenharmony_cinoxoff:
2338c2ecf20Sopenharmony_ci	rcu_read_unlock_bh();
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	err = dev_queue_xmit(skb);
2368c2ecf20Sopenharmony_ci	if (err > 0)
2378c2ecf20Sopenharmony_ci		err = -EIO;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	return err;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/*
2438c2ecf20Sopenharmony_ci * Stuff received packets into the CAIF stack.
2448c2ecf20Sopenharmony_ci * On error, returns non-zero and releases the skb.
2458c2ecf20Sopenharmony_ci */
2468c2ecf20Sopenharmony_cistatic int receive(struct sk_buff *skb, struct net_device *dev,
2478c2ecf20Sopenharmony_ci		   struct packet_type *pkttype, struct net_device *orig_dev)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct cfpkt *pkt;
2508c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd;
2518c2ecf20Sopenharmony_ci	int err;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	rcu_read_lock();
2568c2ecf20Sopenharmony_ci	caifd = caif_get(dev);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
2598c2ecf20Sopenharmony_ci			!netif_oper_up(caifd->netdev)) {
2608c2ecf20Sopenharmony_ci		rcu_read_unlock();
2618c2ecf20Sopenharmony_ci		kfree_skb(skb);
2628c2ecf20Sopenharmony_ci		return NET_RX_DROP;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* Hold reference to netdevice while using CAIF stack */
2668c2ecf20Sopenharmony_ci	caifd_hold(caifd);
2678c2ecf20Sopenharmony_ci	rcu_read_unlock();
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	err = caifd->layer.up->receive(caifd->layer.up, pkt);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/* For -EILSEQ the packet is not freed so so it now */
2728c2ecf20Sopenharmony_ci	if (err == -EILSEQ)
2738c2ecf20Sopenharmony_ci		cfpkt_destroy(pkt);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* Release reference to stack upwards */
2768c2ecf20Sopenharmony_ci	caifd_put(caifd);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (err != 0)
2798c2ecf20Sopenharmony_ci		err = NET_RX_DROP;
2808c2ecf20Sopenharmony_ci	return err;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic struct packet_type caif_packet_type __read_mostly = {
2848c2ecf20Sopenharmony_ci	.type = cpu_to_be16(ETH_P_CAIF),
2858c2ecf20Sopenharmony_ci	.func = receive,
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic void dev_flowctrl(struct net_device *dev, int on)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	rcu_read_lock();
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	caifd = caif_get(dev);
2958c2ecf20Sopenharmony_ci	if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
2968c2ecf20Sopenharmony_ci		rcu_read_unlock();
2978c2ecf20Sopenharmony_ci		return;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	caifd_hold(caifd);
3018c2ecf20Sopenharmony_ci	rcu_read_unlock();
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	caifd->layer.up->ctrlcmd(caifd->layer.up,
3048c2ecf20Sopenharmony_ci				 on ?
3058c2ecf20Sopenharmony_ci				 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
3068c2ecf20Sopenharmony_ci				 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
3078c2ecf20Sopenharmony_ci				 caifd->layer.id);
3088c2ecf20Sopenharmony_ci	caifd_put(caifd);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ciint caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
3128c2ecf20Sopenharmony_ci		     struct cflayer *link_support, int head_room,
3138c2ecf20Sopenharmony_ci		     struct cflayer **layer,
3148c2ecf20Sopenharmony_ci		     int (**rcv_func)(struct sk_buff *, struct net_device *,
3158c2ecf20Sopenharmony_ci				      struct packet_type *,
3168c2ecf20Sopenharmony_ci				      struct net_device *))
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd;
3198c2ecf20Sopenharmony_ci	enum cfcnfg_phy_preference pref;
3208c2ecf20Sopenharmony_ci	struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
3218c2ecf20Sopenharmony_ci	struct caif_device_entry_list *caifdevs;
3228c2ecf20Sopenharmony_ci	int res;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	caifdevs = caif_device_list(dev_net(dev));
3258c2ecf20Sopenharmony_ci	caifd = caif_device_alloc(dev);
3268c2ecf20Sopenharmony_ci	if (!caifd)
3278c2ecf20Sopenharmony_ci		return -ENOMEM;
3288c2ecf20Sopenharmony_ci	*layer = &caifd->layer;
3298c2ecf20Sopenharmony_ci	spin_lock_init(&caifd->flow_lock);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	switch (caifdev->link_select) {
3328c2ecf20Sopenharmony_ci	case CAIF_LINK_HIGH_BANDW:
3338c2ecf20Sopenharmony_ci		pref = CFPHYPREF_HIGH_BW;
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	case CAIF_LINK_LOW_LATENCY:
3368c2ecf20Sopenharmony_ci		pref = CFPHYPREF_LOW_LAT;
3378c2ecf20Sopenharmony_ci		break;
3388c2ecf20Sopenharmony_ci	default:
3398c2ecf20Sopenharmony_ci		pref = CFPHYPREF_HIGH_BW;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci	mutex_lock(&caifdevs->lock);
3438c2ecf20Sopenharmony_ci	list_add_rcu(&caifd->list, &caifdevs->list);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	strlcpy(caifd->layer.name, dev->name,
3468c2ecf20Sopenharmony_ci		sizeof(caifd->layer.name));
3478c2ecf20Sopenharmony_ci	caifd->layer.transmit = transmit;
3488c2ecf20Sopenharmony_ci	res = cfcnfg_add_phy_layer(cfg,
3498c2ecf20Sopenharmony_ci				dev,
3508c2ecf20Sopenharmony_ci				&caifd->layer,
3518c2ecf20Sopenharmony_ci				pref,
3528c2ecf20Sopenharmony_ci				link_support,
3538c2ecf20Sopenharmony_ci				caifdev->use_fcs,
3548c2ecf20Sopenharmony_ci				head_room);
3558c2ecf20Sopenharmony_ci	mutex_unlock(&caifdevs->lock);
3568c2ecf20Sopenharmony_ci	if (rcv_func)
3578c2ecf20Sopenharmony_ci		*rcv_func = receive;
3588c2ecf20Sopenharmony_ci	return res;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(caif_enroll_dev);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci/* notify Caif of device events */
3638c2ecf20Sopenharmony_cistatic int caif_device_notify(struct notifier_block *me, unsigned long what,
3648c2ecf20Sopenharmony_ci			      void *ptr)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
3678c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd = NULL;
3688c2ecf20Sopenharmony_ci	struct caif_dev_common *caifdev;
3698c2ecf20Sopenharmony_ci	struct cfcnfg *cfg;
3708c2ecf20Sopenharmony_ci	struct cflayer *layer, *link_support;
3718c2ecf20Sopenharmony_ci	int head_room = 0;
3728c2ecf20Sopenharmony_ci	struct caif_device_entry_list *caifdevs;
3738c2ecf20Sopenharmony_ci	int res;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	cfg = get_cfcnfg(dev_net(dev));
3768c2ecf20Sopenharmony_ci	caifdevs = caif_device_list(dev_net(dev));
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	caifd = caif_get(dev);
3798c2ecf20Sopenharmony_ci	if (caifd == NULL && dev->type != ARPHRD_CAIF)
3808c2ecf20Sopenharmony_ci		return 0;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	switch (what) {
3838c2ecf20Sopenharmony_ci	case NETDEV_REGISTER:
3848c2ecf20Sopenharmony_ci		if (caifd != NULL)
3858c2ecf20Sopenharmony_ci			break;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		caifdev = netdev_priv(dev);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		link_support = NULL;
3908c2ecf20Sopenharmony_ci		if (caifdev->use_frag) {
3918c2ecf20Sopenharmony_ci			head_room = 1;
3928c2ecf20Sopenharmony_ci			link_support = cfserl_create(dev->ifindex,
3938c2ecf20Sopenharmony_ci							caifdev->use_stx);
3948c2ecf20Sopenharmony_ci			if (!link_support) {
3958c2ecf20Sopenharmony_ci				pr_warn("Out of memory\n");
3968c2ecf20Sopenharmony_ci				break;
3978c2ecf20Sopenharmony_ci			}
3988c2ecf20Sopenharmony_ci		}
3998c2ecf20Sopenharmony_ci		res = caif_enroll_dev(dev, caifdev, link_support, head_room,
4008c2ecf20Sopenharmony_ci				&layer, NULL);
4018c2ecf20Sopenharmony_ci		if (res)
4028c2ecf20Sopenharmony_ci			cfserl_release(link_support);
4038c2ecf20Sopenharmony_ci		caifdev->flowctrl = dev_flowctrl;
4048c2ecf20Sopenharmony_ci		break;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	case NETDEV_UP:
4078c2ecf20Sopenharmony_ci		rcu_read_lock();
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		caifd = caif_get(dev);
4108c2ecf20Sopenharmony_ci		if (caifd == NULL) {
4118c2ecf20Sopenharmony_ci			rcu_read_unlock();
4128c2ecf20Sopenharmony_ci			break;
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		caifd->xoff = false;
4168c2ecf20Sopenharmony_ci		cfcnfg_set_phy_state(cfg, &caifd->layer, true);
4178c2ecf20Sopenharmony_ci		rcu_read_unlock();
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		break;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	case NETDEV_DOWN:
4228c2ecf20Sopenharmony_ci		rcu_read_lock();
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci		caifd = caif_get(dev);
4258c2ecf20Sopenharmony_ci		if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
4268c2ecf20Sopenharmony_ci			rcu_read_unlock();
4278c2ecf20Sopenharmony_ci			return -EINVAL;
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
4318c2ecf20Sopenharmony_ci		caifd_hold(caifd);
4328c2ecf20Sopenharmony_ci		rcu_read_unlock();
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		caifd->layer.up->ctrlcmd(caifd->layer.up,
4358c2ecf20Sopenharmony_ci					 _CAIF_CTRLCMD_PHYIF_DOWN_IND,
4368c2ecf20Sopenharmony_ci					 caifd->layer.id);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		spin_lock_bh(&caifd->flow_lock);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		/*
4418c2ecf20Sopenharmony_ci		 * Replace our xoff-destructor with original destructor.
4428c2ecf20Sopenharmony_ci		 * We trust that skb->destructor *always* is called before
4438c2ecf20Sopenharmony_ci		 * the skb reference is invalid. The hijacked SKB destructor
4448c2ecf20Sopenharmony_ci		 * takes the flow_lock so manipulating the skb->destructor here
4458c2ecf20Sopenharmony_ci		 * should be safe.
4468c2ecf20Sopenharmony_ci		*/
4478c2ecf20Sopenharmony_ci		if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
4488c2ecf20Sopenharmony_ci			caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		caifd->xoff = false;
4518c2ecf20Sopenharmony_ci		caifd->xoff_skb_dtor = NULL;
4528c2ecf20Sopenharmony_ci		caifd->xoff_skb = NULL;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci		spin_unlock_bh(&caifd->flow_lock);
4558c2ecf20Sopenharmony_ci		caifd_put(caifd);
4568c2ecf20Sopenharmony_ci		break;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	case NETDEV_UNREGISTER:
4598c2ecf20Sopenharmony_ci		mutex_lock(&caifdevs->lock);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci		caifd = caif_get(dev);
4628c2ecf20Sopenharmony_ci		if (caifd == NULL) {
4638c2ecf20Sopenharmony_ci			mutex_unlock(&caifdevs->lock);
4648c2ecf20Sopenharmony_ci			break;
4658c2ecf20Sopenharmony_ci		}
4668c2ecf20Sopenharmony_ci		list_del_rcu(&caifd->list);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		/*
4698c2ecf20Sopenharmony_ci		 * NETDEV_UNREGISTER is called repeatedly until all reference
4708c2ecf20Sopenharmony_ci		 * counts for the net-device are released. If references to
4718c2ecf20Sopenharmony_ci		 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
4728c2ecf20Sopenharmony_ci		 * the next call to NETDEV_UNREGISTER.
4738c2ecf20Sopenharmony_ci		 *
4748c2ecf20Sopenharmony_ci		 * If any packets are in flight down the CAIF Stack,
4758c2ecf20Sopenharmony_ci		 * cfcnfg_del_phy_layer will return nonzero.
4768c2ecf20Sopenharmony_ci		 * If no packets are in flight, the CAIF Stack associated
4778c2ecf20Sopenharmony_ci		 * with the net-device un-registering is freed.
4788c2ecf20Sopenharmony_ci		 */
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		if (caifd_refcnt_read(caifd) != 0 ||
4818c2ecf20Sopenharmony_ci			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci			pr_info("Wait for device inuse\n");
4848c2ecf20Sopenharmony_ci			/* Enrole device if CAIF Stack is still in use */
4858c2ecf20Sopenharmony_ci			list_add_rcu(&caifd->list, &caifdevs->list);
4868c2ecf20Sopenharmony_ci			mutex_unlock(&caifdevs->lock);
4878c2ecf20Sopenharmony_ci			break;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		synchronize_rcu();
4918c2ecf20Sopenharmony_ci		dev_put(caifd->netdev);
4928c2ecf20Sopenharmony_ci		free_percpu(caifd->pcpu_refcnt);
4938c2ecf20Sopenharmony_ci		kfree(caifd);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		mutex_unlock(&caifdevs->lock);
4968c2ecf20Sopenharmony_ci		break;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	return 0;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic struct notifier_block caif_device_notifier = {
5028c2ecf20Sopenharmony_ci	.notifier_call = caif_device_notify,
5038c2ecf20Sopenharmony_ci	.priority = 0,
5048c2ecf20Sopenharmony_ci};
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci/* Per-namespace Caif devices handling */
5078c2ecf20Sopenharmony_cistatic int caif_init_net(struct net *net)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	struct caif_net *caifn = net_generic(net, caif_net_id);
5108c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&caifn->caifdevs.list);
5118c2ecf20Sopenharmony_ci	mutex_init(&caifn->caifdevs.lock);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	caifn->cfg = cfcnfg_create();
5148c2ecf20Sopenharmony_ci	if (!caifn->cfg)
5158c2ecf20Sopenharmony_ci		return -ENOMEM;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return 0;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic void caif_exit_net(struct net *net)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct caif_device_entry *caifd, *tmp;
5238c2ecf20Sopenharmony_ci	struct caif_device_entry_list *caifdevs =
5248c2ecf20Sopenharmony_ci	    caif_device_list(net);
5258c2ecf20Sopenharmony_ci	struct cfcnfg *cfg =  get_cfcnfg(net);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	rtnl_lock();
5288c2ecf20Sopenharmony_ci	mutex_lock(&caifdevs->lock);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
5318c2ecf20Sopenharmony_ci		int i = 0;
5328c2ecf20Sopenharmony_ci		list_del_rcu(&caifd->list);
5338c2ecf20Sopenharmony_ci		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		while (i < 10 &&
5368c2ecf20Sopenharmony_ci			(caifd_refcnt_read(caifd) != 0 ||
5378c2ecf20Sopenharmony_ci			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci			pr_info("Wait for device inuse\n");
5408c2ecf20Sopenharmony_ci			msleep(250);
5418c2ecf20Sopenharmony_ci			i++;
5428c2ecf20Sopenharmony_ci		}
5438c2ecf20Sopenharmony_ci		synchronize_rcu();
5448c2ecf20Sopenharmony_ci		dev_put(caifd->netdev);
5458c2ecf20Sopenharmony_ci		free_percpu(caifd->pcpu_refcnt);
5468c2ecf20Sopenharmony_ci		kfree(caifd);
5478c2ecf20Sopenharmony_ci	}
5488c2ecf20Sopenharmony_ci	cfcnfg_remove(cfg);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	mutex_unlock(&caifdevs->lock);
5518c2ecf20Sopenharmony_ci	rtnl_unlock();
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic struct pernet_operations caif_net_ops = {
5558c2ecf20Sopenharmony_ci	.init = caif_init_net,
5568c2ecf20Sopenharmony_ci	.exit = caif_exit_net,
5578c2ecf20Sopenharmony_ci	.id   = &caif_net_id,
5588c2ecf20Sopenharmony_ci	.size = sizeof(struct caif_net),
5598c2ecf20Sopenharmony_ci};
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci/* Initialize Caif devices list */
5628c2ecf20Sopenharmony_cistatic int __init caif_device_init(void)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	int result;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	result = register_pernet_subsys(&caif_net_ops);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (result)
5698c2ecf20Sopenharmony_ci		return result;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	register_netdevice_notifier(&caif_device_notifier);
5728c2ecf20Sopenharmony_ci	dev_add_pack(&caif_packet_type);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	return result;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cistatic void __exit caif_device_exit(void)
5788c2ecf20Sopenharmony_ci{
5798c2ecf20Sopenharmony_ci	unregister_netdevice_notifier(&caif_device_notifier);
5808c2ecf20Sopenharmony_ci	dev_remove_pack(&caif_packet_type);
5818c2ecf20Sopenharmony_ci	unregister_pernet_subsys(&caif_net_ops);
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_cimodule_init(caif_device_init);
5858c2ecf20Sopenharmony_cimodule_exit(caif_device_exit);
586