162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CAIF Interface registration.
462306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010
562306a36Sopenharmony_ci * Author:	Sjur Brendeland
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont
862306a36Sopenharmony_ci *  and Sakari Ailus <sakari.ailus@nokia.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/if_arp.h>
1562306a36Sopenharmony_ci#include <linux/net.h>
1662306a36Sopenharmony_ci#include <linux/netdevice.h>
1762306a36Sopenharmony_ci#include <linux/mutex.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/spinlock.h>
2062306a36Sopenharmony_ci#include <net/netns/generic.h>
2162306a36Sopenharmony_ci#include <net/net_namespace.h>
2262306a36Sopenharmony_ci#include <net/pkt_sched.h>
2362306a36Sopenharmony_ci#include <net/caif/caif_device.h>
2462306a36Sopenharmony_ci#include <net/caif/caif_layer.h>
2562306a36Sopenharmony_ci#include <net/caif/caif_dev.h>
2662306a36Sopenharmony_ci#include <net/caif/cfpkt.h>
2762306a36Sopenharmony_ci#include <net/caif/cfcnfg.h>
2862306a36Sopenharmony_ci#include <net/caif/cfserl.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/* Used for local tracking of the CAIF net devices */
3362306a36Sopenharmony_cistruct caif_device_entry {
3462306a36Sopenharmony_ci	struct cflayer layer;
3562306a36Sopenharmony_ci	struct list_head list;
3662306a36Sopenharmony_ci	struct net_device *netdev;
3762306a36Sopenharmony_ci	int __percpu *pcpu_refcnt;
3862306a36Sopenharmony_ci	spinlock_t flow_lock;
3962306a36Sopenharmony_ci	struct sk_buff *xoff_skb;
4062306a36Sopenharmony_ci	void (*xoff_skb_dtor)(struct sk_buff *skb);
4162306a36Sopenharmony_ci	bool xoff;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct caif_device_entry_list {
4562306a36Sopenharmony_ci	struct list_head list;
4662306a36Sopenharmony_ci	/* Protects simulanous deletes in list */
4762306a36Sopenharmony_ci	struct mutex lock;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistruct caif_net {
5162306a36Sopenharmony_ci	struct cfcnfg *cfg;
5262306a36Sopenharmony_ci	struct caif_device_entry_list caifdevs;
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic unsigned int caif_net_id;
5662306a36Sopenharmony_cistatic int q_high = 50; /* Percent */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct cfcnfg *get_cfcnfg(struct net *net)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct caif_net *caifn;
6162306a36Sopenharmony_ci	caifn = net_generic(net, caif_net_id);
6262306a36Sopenharmony_ci	return caifn->cfg;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ciEXPORT_SYMBOL(get_cfcnfg);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic struct caif_device_entry_list *caif_device_list(struct net *net)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct caif_net *caifn;
6962306a36Sopenharmony_ci	caifn = net_generic(net, caif_net_id);
7062306a36Sopenharmony_ci	return &caifn->caifdevs;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void caifd_put(struct caif_device_entry *e)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	this_cpu_dec(*e->pcpu_refcnt);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic void caifd_hold(struct caif_device_entry *e)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	this_cpu_inc(*e->pcpu_refcnt);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int caifd_refcnt_read(struct caif_device_entry *e)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int i, refcnt = 0;
8662306a36Sopenharmony_ci	for_each_possible_cpu(i)
8762306a36Sopenharmony_ci		refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
8862306a36Sopenharmony_ci	return refcnt;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Allocate new CAIF device. */
9262306a36Sopenharmony_cistatic struct caif_device_entry *caif_device_alloc(struct net_device *dev)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	struct caif_device_entry *caifd;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	caifd = kzalloc(sizeof(*caifd), GFP_KERNEL);
9762306a36Sopenharmony_ci	if (!caifd)
9862306a36Sopenharmony_ci		return NULL;
9962306a36Sopenharmony_ci	caifd->pcpu_refcnt = alloc_percpu(int);
10062306a36Sopenharmony_ci	if (!caifd->pcpu_refcnt) {
10162306a36Sopenharmony_ci		kfree(caifd);
10262306a36Sopenharmony_ci		return NULL;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci	caifd->netdev = dev;
10562306a36Sopenharmony_ci	dev_hold(dev);
10662306a36Sopenharmony_ci	return caifd;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic struct caif_device_entry *caif_get(struct net_device *dev)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct caif_device_entry_list *caifdevs =
11262306a36Sopenharmony_ci	    caif_device_list(dev_net(dev));
11362306a36Sopenharmony_ci	struct caif_device_entry *caifd;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	list_for_each_entry_rcu(caifd, &caifdevs->list, list,
11662306a36Sopenharmony_ci				lockdep_rtnl_is_held()) {
11762306a36Sopenharmony_ci		if (caifd->netdev == dev)
11862306a36Sopenharmony_ci			return caifd;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci	return NULL;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void caif_flow_cb(struct sk_buff *skb)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct caif_device_entry *caifd;
12662306a36Sopenharmony_ci	void (*dtor)(struct sk_buff *skb) = NULL;
12762306a36Sopenharmony_ci	bool send_xoff;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	WARN_ON(skb->dev == NULL);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	rcu_read_lock();
13262306a36Sopenharmony_ci	caifd = caif_get(skb->dev);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	WARN_ON(caifd == NULL);
13562306a36Sopenharmony_ci	if (!caifd) {
13662306a36Sopenharmony_ci		rcu_read_unlock();
13762306a36Sopenharmony_ci		return;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	caifd_hold(caifd);
14162306a36Sopenharmony_ci	rcu_read_unlock();
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	spin_lock_bh(&caifd->flow_lock);
14462306a36Sopenharmony_ci	send_xoff = caifd->xoff;
14562306a36Sopenharmony_ci	caifd->xoff = false;
14662306a36Sopenharmony_ci	dtor = caifd->xoff_skb_dtor;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (WARN_ON(caifd->xoff_skb != skb))
14962306a36Sopenharmony_ci		skb = NULL;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	caifd->xoff_skb = NULL;
15262306a36Sopenharmony_ci	caifd->xoff_skb_dtor = NULL;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	spin_unlock_bh(&caifd->flow_lock);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (dtor && skb)
15762306a36Sopenharmony_ci		dtor(skb);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (send_xoff)
16062306a36Sopenharmony_ci		caifd->layer.up->
16162306a36Sopenharmony_ci			ctrlcmd(caifd->layer.up,
16262306a36Sopenharmony_ci				_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
16362306a36Sopenharmony_ci				caifd->layer.id);
16462306a36Sopenharmony_ci	caifd_put(caifd);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic int transmit(struct cflayer *layer, struct cfpkt *pkt)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	int err, high = 0, qlen = 0;
17062306a36Sopenharmony_ci	struct caif_device_entry *caifd =
17162306a36Sopenharmony_ci	    container_of(layer, struct caif_device_entry, layer);
17262306a36Sopenharmony_ci	struct sk_buff *skb;
17362306a36Sopenharmony_ci	struct netdev_queue *txq;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	rcu_read_lock_bh();
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	skb = cfpkt_tonative(pkt);
17862306a36Sopenharmony_ci	skb->dev = caifd->netdev;
17962306a36Sopenharmony_ci	skb_reset_network_header(skb);
18062306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_CAIF);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/* Check if we need to handle xoff */
18362306a36Sopenharmony_ci	if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE))
18462306a36Sopenharmony_ci		goto noxoff;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	if (unlikely(caifd->xoff))
18762306a36Sopenharmony_ci		goto noxoff;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (likely(!netif_queue_stopped(caifd->netdev))) {
19062306a36Sopenharmony_ci		struct Qdisc *sch;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		/* If we run with a TX queue, check if the queue is too long*/
19362306a36Sopenharmony_ci		txq = netdev_get_tx_queue(skb->dev, 0);
19462306a36Sopenharmony_ci		sch = rcu_dereference_bh(txq->qdisc);
19562306a36Sopenharmony_ci		if (likely(qdisc_is_empty(sch)))
19662306a36Sopenharmony_ci			goto noxoff;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		/* can check for explicit qdisc len value only !NOLOCK,
19962306a36Sopenharmony_ci		 * always set flow off otherwise
20062306a36Sopenharmony_ci		 */
20162306a36Sopenharmony_ci		high = (caifd->netdev->tx_queue_len * q_high) / 100;
20262306a36Sopenharmony_ci		if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high))
20362306a36Sopenharmony_ci			goto noxoff;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Hold lock while accessing xoff */
20762306a36Sopenharmony_ci	spin_lock_bh(&caifd->flow_lock);
20862306a36Sopenharmony_ci	if (caifd->xoff) {
20962306a36Sopenharmony_ci		spin_unlock_bh(&caifd->flow_lock);
21062306a36Sopenharmony_ci		goto noxoff;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/*
21462306a36Sopenharmony_ci	 * Handle flow off, we do this by temporary hi-jacking this
21562306a36Sopenharmony_ci	 * skb's destructor function, and replace it with our own
21662306a36Sopenharmony_ci	 * flow-on callback. The callback will set flow-on and call
21762306a36Sopenharmony_ci	 * the original destructor.
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
22162306a36Sopenharmony_ci			netif_queue_stopped(caifd->netdev),
22262306a36Sopenharmony_ci			qlen, high);
22362306a36Sopenharmony_ci	caifd->xoff = true;
22462306a36Sopenharmony_ci	caifd->xoff_skb = skb;
22562306a36Sopenharmony_ci	caifd->xoff_skb_dtor = skb->destructor;
22662306a36Sopenharmony_ci	skb->destructor = caif_flow_cb;
22762306a36Sopenharmony_ci	spin_unlock_bh(&caifd->flow_lock);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	caifd->layer.up->ctrlcmd(caifd->layer.up,
23062306a36Sopenharmony_ci					_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
23162306a36Sopenharmony_ci					caifd->layer.id);
23262306a36Sopenharmony_cinoxoff:
23362306a36Sopenharmony_ci	rcu_read_unlock_bh();
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	err = dev_queue_xmit(skb);
23662306a36Sopenharmony_ci	if (err > 0)
23762306a36Sopenharmony_ci		err = -EIO;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return err;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/*
24362306a36Sopenharmony_ci * Stuff received packets into the CAIF stack.
24462306a36Sopenharmony_ci * On error, returns non-zero and releases the skb.
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_cistatic int receive(struct sk_buff *skb, struct net_device *dev,
24762306a36Sopenharmony_ci		   struct packet_type *pkttype, struct net_device *orig_dev)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct cfpkt *pkt;
25062306a36Sopenharmony_ci	struct caif_device_entry *caifd;
25162306a36Sopenharmony_ci	int err;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	rcu_read_lock();
25662306a36Sopenharmony_ci	caifd = caif_get(dev);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
25962306a36Sopenharmony_ci			!netif_oper_up(caifd->netdev)) {
26062306a36Sopenharmony_ci		rcu_read_unlock();
26162306a36Sopenharmony_ci		kfree_skb(skb);
26262306a36Sopenharmony_ci		return NET_RX_DROP;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* Hold reference to netdevice while using CAIF stack */
26662306a36Sopenharmony_ci	caifd_hold(caifd);
26762306a36Sopenharmony_ci	rcu_read_unlock();
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	err = caifd->layer.up->receive(caifd->layer.up, pkt);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* For -EILSEQ the packet is not freed so free it now */
27262306a36Sopenharmony_ci	if (err == -EILSEQ)
27362306a36Sopenharmony_ci		cfpkt_destroy(pkt);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* Release reference to stack upwards */
27662306a36Sopenharmony_ci	caifd_put(caifd);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (err != 0)
27962306a36Sopenharmony_ci		err = NET_RX_DROP;
28062306a36Sopenharmony_ci	return err;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic struct packet_type caif_packet_type __read_mostly = {
28462306a36Sopenharmony_ci	.type = cpu_to_be16(ETH_P_CAIF),
28562306a36Sopenharmony_ci	.func = receive,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void dev_flowctrl(struct net_device *dev, int on)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct caif_device_entry *caifd;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	rcu_read_lock();
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	caifd = caif_get(dev);
29562306a36Sopenharmony_ci	if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
29662306a36Sopenharmony_ci		rcu_read_unlock();
29762306a36Sopenharmony_ci		return;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	caifd_hold(caifd);
30162306a36Sopenharmony_ci	rcu_read_unlock();
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	caifd->layer.up->ctrlcmd(caifd->layer.up,
30462306a36Sopenharmony_ci				 on ?
30562306a36Sopenharmony_ci				 _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
30662306a36Sopenharmony_ci				 _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
30762306a36Sopenharmony_ci				 caifd->layer.id);
30862306a36Sopenharmony_ci	caifd_put(caifd);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ciint caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
31262306a36Sopenharmony_ci		     struct cflayer *link_support, int head_room,
31362306a36Sopenharmony_ci		     struct cflayer **layer,
31462306a36Sopenharmony_ci		     int (**rcv_func)(struct sk_buff *, struct net_device *,
31562306a36Sopenharmony_ci				      struct packet_type *,
31662306a36Sopenharmony_ci				      struct net_device *))
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct caif_device_entry *caifd;
31962306a36Sopenharmony_ci	enum cfcnfg_phy_preference pref;
32062306a36Sopenharmony_ci	struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
32162306a36Sopenharmony_ci	struct caif_device_entry_list *caifdevs;
32262306a36Sopenharmony_ci	int res;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	caifdevs = caif_device_list(dev_net(dev));
32562306a36Sopenharmony_ci	caifd = caif_device_alloc(dev);
32662306a36Sopenharmony_ci	if (!caifd)
32762306a36Sopenharmony_ci		return -ENOMEM;
32862306a36Sopenharmony_ci	*layer = &caifd->layer;
32962306a36Sopenharmony_ci	spin_lock_init(&caifd->flow_lock);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	switch (caifdev->link_select) {
33262306a36Sopenharmony_ci	case CAIF_LINK_HIGH_BANDW:
33362306a36Sopenharmony_ci		pref = CFPHYPREF_HIGH_BW;
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case CAIF_LINK_LOW_LATENCY:
33662306a36Sopenharmony_ci		pref = CFPHYPREF_LOW_LAT;
33762306a36Sopenharmony_ci		break;
33862306a36Sopenharmony_ci	default:
33962306a36Sopenharmony_ci		pref = CFPHYPREF_HIGH_BW;
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci	mutex_lock(&caifdevs->lock);
34362306a36Sopenharmony_ci	list_add_rcu(&caifd->list, &caifdevs->list);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	strscpy(caifd->layer.name, dev->name,
34662306a36Sopenharmony_ci		sizeof(caifd->layer.name));
34762306a36Sopenharmony_ci	caifd->layer.transmit = transmit;
34862306a36Sopenharmony_ci	res = cfcnfg_add_phy_layer(cfg,
34962306a36Sopenharmony_ci				dev,
35062306a36Sopenharmony_ci				&caifd->layer,
35162306a36Sopenharmony_ci				pref,
35262306a36Sopenharmony_ci				link_support,
35362306a36Sopenharmony_ci				caifdev->use_fcs,
35462306a36Sopenharmony_ci				head_room);
35562306a36Sopenharmony_ci	mutex_unlock(&caifdevs->lock);
35662306a36Sopenharmony_ci	if (rcv_func)
35762306a36Sopenharmony_ci		*rcv_func = receive;
35862306a36Sopenharmony_ci	return res;
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ciEXPORT_SYMBOL(caif_enroll_dev);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/* notify Caif of device events */
36362306a36Sopenharmony_cistatic int caif_device_notify(struct notifier_block *me, unsigned long what,
36462306a36Sopenharmony_ci			      void *ptr)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
36762306a36Sopenharmony_ci	struct caif_device_entry *caifd = NULL;
36862306a36Sopenharmony_ci	struct caif_dev_common *caifdev;
36962306a36Sopenharmony_ci	struct cfcnfg *cfg;
37062306a36Sopenharmony_ci	struct cflayer *layer, *link_support;
37162306a36Sopenharmony_ci	int head_room = 0;
37262306a36Sopenharmony_ci	struct caif_device_entry_list *caifdevs;
37362306a36Sopenharmony_ci	int res;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	cfg = get_cfcnfg(dev_net(dev));
37662306a36Sopenharmony_ci	caifdevs = caif_device_list(dev_net(dev));
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	caifd = caif_get(dev);
37962306a36Sopenharmony_ci	if (caifd == NULL && dev->type != ARPHRD_CAIF)
38062306a36Sopenharmony_ci		return 0;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	switch (what) {
38362306a36Sopenharmony_ci	case NETDEV_REGISTER:
38462306a36Sopenharmony_ci		if (caifd != NULL)
38562306a36Sopenharmony_ci			break;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		caifdev = netdev_priv(dev);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		link_support = NULL;
39062306a36Sopenharmony_ci		if (caifdev->use_frag) {
39162306a36Sopenharmony_ci			head_room = 1;
39262306a36Sopenharmony_ci			link_support = cfserl_create(dev->ifindex,
39362306a36Sopenharmony_ci							caifdev->use_stx);
39462306a36Sopenharmony_ci			if (!link_support) {
39562306a36Sopenharmony_ci				pr_warn("Out of memory\n");
39662306a36Sopenharmony_ci				break;
39762306a36Sopenharmony_ci			}
39862306a36Sopenharmony_ci		}
39962306a36Sopenharmony_ci		res = caif_enroll_dev(dev, caifdev, link_support, head_room,
40062306a36Sopenharmony_ci				&layer, NULL);
40162306a36Sopenharmony_ci		if (res)
40262306a36Sopenharmony_ci			cfserl_release(link_support);
40362306a36Sopenharmony_ci		caifdev->flowctrl = dev_flowctrl;
40462306a36Sopenharmony_ci		break;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	case NETDEV_UP:
40762306a36Sopenharmony_ci		rcu_read_lock();
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		caifd = caif_get(dev);
41062306a36Sopenharmony_ci		if (caifd == NULL) {
41162306a36Sopenharmony_ci			rcu_read_unlock();
41262306a36Sopenharmony_ci			break;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		caifd->xoff = false;
41662306a36Sopenharmony_ci		cfcnfg_set_phy_state(cfg, &caifd->layer, true);
41762306a36Sopenharmony_ci		rcu_read_unlock();
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci		break;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	case NETDEV_DOWN:
42262306a36Sopenharmony_ci		rcu_read_lock();
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci		caifd = caif_get(dev);
42562306a36Sopenharmony_ci		if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
42662306a36Sopenharmony_ci			rcu_read_unlock();
42762306a36Sopenharmony_ci			return -EINVAL;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
43162306a36Sopenharmony_ci		caifd_hold(caifd);
43262306a36Sopenharmony_ci		rcu_read_unlock();
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci		caifd->layer.up->ctrlcmd(caifd->layer.up,
43562306a36Sopenharmony_ci					 _CAIF_CTRLCMD_PHYIF_DOWN_IND,
43662306a36Sopenharmony_ci					 caifd->layer.id);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		spin_lock_bh(&caifd->flow_lock);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		/*
44162306a36Sopenharmony_ci		 * Replace our xoff-destructor with original destructor.
44262306a36Sopenharmony_ci		 * We trust that skb->destructor *always* is called before
44362306a36Sopenharmony_ci		 * the skb reference is invalid. The hijacked SKB destructor
44462306a36Sopenharmony_ci		 * takes the flow_lock so manipulating the skb->destructor here
44562306a36Sopenharmony_ci		 * should be safe.
44662306a36Sopenharmony_ci		*/
44762306a36Sopenharmony_ci		if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
44862306a36Sopenharmony_ci			caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci		caifd->xoff = false;
45162306a36Sopenharmony_ci		caifd->xoff_skb_dtor = NULL;
45262306a36Sopenharmony_ci		caifd->xoff_skb = NULL;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		spin_unlock_bh(&caifd->flow_lock);
45562306a36Sopenharmony_ci		caifd_put(caifd);
45662306a36Sopenharmony_ci		break;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	case NETDEV_UNREGISTER:
45962306a36Sopenharmony_ci		mutex_lock(&caifdevs->lock);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		caifd = caif_get(dev);
46262306a36Sopenharmony_ci		if (caifd == NULL) {
46362306a36Sopenharmony_ci			mutex_unlock(&caifdevs->lock);
46462306a36Sopenharmony_ci			break;
46562306a36Sopenharmony_ci		}
46662306a36Sopenharmony_ci		list_del_rcu(&caifd->list);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		/*
46962306a36Sopenharmony_ci		 * NETDEV_UNREGISTER is called repeatedly until all reference
47062306a36Sopenharmony_ci		 * counts for the net-device are released. If references to
47162306a36Sopenharmony_ci		 * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
47262306a36Sopenharmony_ci		 * the next call to NETDEV_UNREGISTER.
47362306a36Sopenharmony_ci		 *
47462306a36Sopenharmony_ci		 * If any packets are in flight down the CAIF Stack,
47562306a36Sopenharmony_ci		 * cfcnfg_del_phy_layer will return nonzero.
47662306a36Sopenharmony_ci		 * If no packets are in flight, the CAIF Stack associated
47762306a36Sopenharmony_ci		 * with the net-device un-registering is freed.
47862306a36Sopenharmony_ci		 */
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		if (caifd_refcnt_read(caifd) != 0 ||
48162306a36Sopenharmony_ci			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci			pr_info("Wait for device inuse\n");
48462306a36Sopenharmony_ci			/* Enrole device if CAIF Stack is still in use */
48562306a36Sopenharmony_ci			list_add_rcu(&caifd->list, &caifdevs->list);
48662306a36Sopenharmony_ci			mutex_unlock(&caifdevs->lock);
48762306a36Sopenharmony_ci			break;
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci		synchronize_rcu();
49162306a36Sopenharmony_ci		dev_put(caifd->netdev);
49262306a36Sopenharmony_ci		free_percpu(caifd->pcpu_refcnt);
49362306a36Sopenharmony_ci		kfree(caifd);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		mutex_unlock(&caifdevs->lock);
49662306a36Sopenharmony_ci		break;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	return 0;
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic struct notifier_block caif_device_notifier = {
50262306a36Sopenharmony_ci	.notifier_call = caif_device_notify,
50362306a36Sopenharmony_ci	.priority = 0,
50462306a36Sopenharmony_ci};
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/* Per-namespace Caif devices handling */
50762306a36Sopenharmony_cistatic int caif_init_net(struct net *net)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct caif_net *caifn = net_generic(net, caif_net_id);
51062306a36Sopenharmony_ci	INIT_LIST_HEAD(&caifn->caifdevs.list);
51162306a36Sopenharmony_ci	mutex_init(&caifn->caifdevs.lock);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	caifn->cfg = cfcnfg_create();
51462306a36Sopenharmony_ci	if (!caifn->cfg)
51562306a36Sopenharmony_ci		return -ENOMEM;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_cistatic void caif_exit_net(struct net *net)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct caif_device_entry *caifd, *tmp;
52362306a36Sopenharmony_ci	struct caif_device_entry_list *caifdevs =
52462306a36Sopenharmony_ci	    caif_device_list(net);
52562306a36Sopenharmony_ci	struct cfcnfg *cfg =  get_cfcnfg(net);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	rtnl_lock();
52862306a36Sopenharmony_ci	mutex_lock(&caifdevs->lock);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
53162306a36Sopenharmony_ci		int i = 0;
53262306a36Sopenharmony_ci		list_del_rcu(&caifd->list);
53362306a36Sopenharmony_ci		cfcnfg_set_phy_state(cfg, &caifd->layer, false);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci		while (i < 10 &&
53662306a36Sopenharmony_ci			(caifd_refcnt_read(caifd) != 0 ||
53762306a36Sopenharmony_ci			cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci			pr_info("Wait for device inuse\n");
54062306a36Sopenharmony_ci			msleep(250);
54162306a36Sopenharmony_ci			i++;
54262306a36Sopenharmony_ci		}
54362306a36Sopenharmony_ci		synchronize_rcu();
54462306a36Sopenharmony_ci		dev_put(caifd->netdev);
54562306a36Sopenharmony_ci		free_percpu(caifd->pcpu_refcnt);
54662306a36Sopenharmony_ci		kfree(caifd);
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci	cfcnfg_remove(cfg);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	mutex_unlock(&caifdevs->lock);
55162306a36Sopenharmony_ci	rtnl_unlock();
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic struct pernet_operations caif_net_ops = {
55562306a36Sopenharmony_ci	.init = caif_init_net,
55662306a36Sopenharmony_ci	.exit = caif_exit_net,
55762306a36Sopenharmony_ci	.id   = &caif_net_id,
55862306a36Sopenharmony_ci	.size = sizeof(struct caif_net),
55962306a36Sopenharmony_ci};
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci/* Initialize Caif devices list */
56262306a36Sopenharmony_cistatic int __init caif_device_init(void)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	int result;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	result = register_pernet_subsys(&caif_net_ops);
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (result)
56962306a36Sopenharmony_ci		return result;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	register_netdevice_notifier(&caif_device_notifier);
57262306a36Sopenharmony_ci	dev_add_pack(&caif_packet_type);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return result;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic void __exit caif_device_exit(void)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	unregister_netdevice_notifier(&caif_device_notifier);
58062306a36Sopenharmony_ci	dev_remove_pack(&caif_packet_type);
58162306a36Sopenharmony_ci	unregister_pernet_subsys(&caif_net_ops);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cimodule_init(caif_device_init);
58562306a36Sopenharmony_cimodule_exit(caif_device_exit);
586