xref: /openbmc/linux/net/caif/cfmuxl.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*af873fceSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b482cd20SSjur Braendeland /*
3b482cd20SSjur Braendeland  * Copyright (C) ST-Ericsson AB 2010
426ee65e6Ssjur.brandeland@stericsson.com  * Author:	Sjur Brendeland
5b482cd20SSjur Braendeland  */
6b31fa5baSJoe Perches 
7b31fa5baSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
8b31fa5baSJoe Perches 
9b482cd20SSjur Braendeland #include <linux/stddef.h>
10b482cd20SSjur Braendeland #include <linux/spinlock.h>
11b482cd20SSjur Braendeland #include <linux/slab.h>
120b1e9738Ssjur.brandeland@stericsson.com #include <linux/rculist.h>
13b482cd20SSjur Braendeland #include <net/caif/cfpkt.h>
14b482cd20SSjur Braendeland #include <net/caif/cfmuxl.h>
15b482cd20SSjur Braendeland #include <net/caif/cfsrvl.h>
16b482cd20SSjur Braendeland #include <net/caif/cffrml.h>
17b482cd20SSjur Braendeland 
18b482cd20SSjur Braendeland #define container_obj(layr) container_of(layr, struct cfmuxl, layer)
19b482cd20SSjur Braendeland 
20b482cd20SSjur Braendeland #define CAIF_CTRL_CHANNEL 0
21b482cd20SSjur Braendeland #define UP_CACHE_SIZE 8
22b482cd20SSjur Braendeland #define DN_CACHE_SIZE 8
23b482cd20SSjur Braendeland 
24b482cd20SSjur Braendeland struct cfmuxl {
25b482cd20SSjur Braendeland 	struct cflayer layer;
26b482cd20SSjur Braendeland 	struct list_head srvl_list;
27b482cd20SSjur Braendeland 	struct list_head frml_list;
28b482cd20SSjur Braendeland 	struct cflayer *up_cache[UP_CACHE_SIZE];
29b482cd20SSjur Braendeland 	struct cflayer *dn_cache[DN_CACHE_SIZE];
30b482cd20SSjur Braendeland 	/*
31b482cd20SSjur Braendeland 	 * Set when inserting or removing downwards layers.
32b482cd20SSjur Braendeland 	 */
33b482cd20SSjur Braendeland 	spinlock_t transmit_lock;
34b482cd20SSjur Braendeland 
35b482cd20SSjur Braendeland 	/*
36b482cd20SSjur Braendeland 	 * Set when inserting or removing upwards layers.
37b482cd20SSjur Braendeland 	 */
38b482cd20SSjur Braendeland 	spinlock_t receive_lock;
39b482cd20SSjur Braendeland 
40b482cd20SSjur Braendeland };
41b482cd20SSjur Braendeland 
42b482cd20SSjur Braendeland static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt);
43b482cd20SSjur Braendeland static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt);
44b482cd20SSjur Braendeland static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
45b482cd20SSjur Braendeland 			   int phyid);
46b482cd20SSjur Braendeland static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);
47b482cd20SSjur Braendeland 
cfmuxl_create(void)48b482cd20SSjur Braendeland struct cflayer *cfmuxl_create(void)
49b482cd20SSjur Braendeland {
506ff1e1e3SFabian Frederick 	struct cfmuxl *this = kzalloc(sizeof(struct cfmuxl), GFP_ATOMIC);
516ff1e1e3SFabian Frederick 
52b482cd20SSjur Braendeland 	if (!this)
53b482cd20SSjur Braendeland 		return NULL;
54b482cd20SSjur Braendeland 	this->layer.receive = cfmuxl_receive;
55b482cd20SSjur Braendeland 	this->layer.transmit = cfmuxl_transmit;
56b482cd20SSjur Braendeland 	this->layer.ctrlcmd = cfmuxl_ctrlcmd;
57b482cd20SSjur Braendeland 	INIT_LIST_HEAD(&this->srvl_list);
58b482cd20SSjur Braendeland 	INIT_LIST_HEAD(&this->frml_list);
59b482cd20SSjur Braendeland 	spin_lock_init(&this->transmit_lock);
60b482cd20SSjur Braendeland 	spin_lock_init(&this->receive_lock);
61b482cd20SSjur Braendeland 	snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux");
62b482cd20SSjur Braendeland 	return &this->layer;
63b482cd20SSjur Braendeland }
64b482cd20SSjur Braendeland 
cfmuxl_set_dnlayer(struct cflayer * layr,struct cflayer * dn,u8 phyid)65b482cd20SSjur Braendeland int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
66b482cd20SSjur Braendeland {
67b482cd20SSjur Braendeland 	struct cfmuxl *muxl = (struct cfmuxl *) layr;
680b1e9738Ssjur.brandeland@stericsson.com 
690b1e9738Ssjur.brandeland@stericsson.com 	spin_lock_bh(&muxl->transmit_lock);
700b1e9738Ssjur.brandeland@stericsson.com 	list_add_rcu(&dn->node, &muxl->frml_list);
710b1e9738Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&muxl->transmit_lock);
72b482cd20SSjur Braendeland 	return 0;
73b482cd20SSjur Braendeland }
74b482cd20SSjur Braendeland 
get_from_id(struct list_head * list,u16 id)75b482cd20SSjur Braendeland static struct cflayer *get_from_id(struct list_head *list, u16 id)
76b482cd20SSjur Braendeland {
770b1e9738Ssjur.brandeland@stericsson.com 	struct cflayer *lyr;
780b1e9738Ssjur.brandeland@stericsson.com 	list_for_each_entry_rcu(lyr, list, node) {
790b1e9738Ssjur.brandeland@stericsson.com 		if (lyr->id == id)
800b1e9738Ssjur.brandeland@stericsson.com 			return lyr;
81b482cd20SSjur Braendeland 	}
820b1e9738Ssjur.brandeland@stericsson.com 
83b482cd20SSjur Braendeland 	return NULL;
84b482cd20SSjur Braendeland }
85b482cd20SSjur Braendeland 
cfmuxl_set_uplayer(struct cflayer * layr,struct cflayer * up,u8 linkid)8654e90fb5Ssjur.brandeland@stericsson.com int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
8754e90fb5Ssjur.brandeland@stericsson.com {
8854e90fb5Ssjur.brandeland@stericsson.com 	struct cfmuxl *muxl = container_obj(layr);
8954e90fb5Ssjur.brandeland@stericsson.com 	struct cflayer *old;
9054e90fb5Ssjur.brandeland@stericsson.com 
9154e90fb5Ssjur.brandeland@stericsson.com 	spin_lock_bh(&muxl->receive_lock);
9254e90fb5Ssjur.brandeland@stericsson.com 
9354e90fb5Ssjur.brandeland@stericsson.com 	/* Two entries with same id is wrong, so remove old layer from mux */
9454e90fb5Ssjur.brandeland@stericsson.com 	old = get_from_id(&muxl->srvl_list, linkid);
9554e90fb5Ssjur.brandeland@stericsson.com 	if (old != NULL)
9654e90fb5Ssjur.brandeland@stericsson.com 		list_del_rcu(&old->node);
9754e90fb5Ssjur.brandeland@stericsson.com 
9854e90fb5Ssjur.brandeland@stericsson.com 	list_add_rcu(&up->node, &muxl->srvl_list);
9954e90fb5Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&muxl->receive_lock);
10054e90fb5Ssjur.brandeland@stericsson.com 
10154e90fb5Ssjur.brandeland@stericsson.com 	return 0;
10254e90fb5Ssjur.brandeland@stericsson.com }
10354e90fb5Ssjur.brandeland@stericsson.com 
cfmuxl_remove_dnlayer(struct cflayer * layr,u8 phyid)104b482cd20SSjur Braendeland struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)
105b482cd20SSjur Braendeland {
106b482cd20SSjur Braendeland 	struct cfmuxl *muxl = container_obj(layr);
107b482cd20SSjur Braendeland 	struct cflayer *dn;
1080b1e9738Ssjur.brandeland@stericsson.com 	int idx = phyid % DN_CACHE_SIZE;
1090b1e9738Ssjur.brandeland@stericsson.com 
1100b1e9738Ssjur.brandeland@stericsson.com 	spin_lock_bh(&muxl->transmit_lock);
111a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(muxl->dn_cache[idx], NULL);
112b482cd20SSjur Braendeland 	dn = get_from_id(&muxl->frml_list, phyid);
1130b1e9738Ssjur.brandeland@stericsson.com 	if (dn == NULL)
1140b1e9738Ssjur.brandeland@stericsson.com 		goto out;
1150b1e9738Ssjur.brandeland@stericsson.com 
1160b1e9738Ssjur.brandeland@stericsson.com 	list_del_rcu(&dn->node);
117b482cd20SSjur Braendeland 	caif_assert(dn != NULL);
1180b1e9738Ssjur.brandeland@stericsson.com out:
1190b1e9738Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&muxl->transmit_lock);
120b482cd20SSjur Braendeland 	return dn;
121b482cd20SSjur Braendeland }
122b482cd20SSjur Braendeland 
get_up(struct cfmuxl * muxl,u16 id)123b482cd20SSjur Braendeland static struct cflayer *get_up(struct cfmuxl *muxl, u16 id)
124b482cd20SSjur Braendeland {
125b482cd20SSjur Braendeland 	struct cflayer *up;
126b482cd20SSjur Braendeland 	int idx = id % UP_CACHE_SIZE;
1270b1e9738Ssjur.brandeland@stericsson.com 	up = rcu_dereference(muxl->up_cache[idx]);
128b482cd20SSjur Braendeland 	if (up == NULL || up->id != id) {
1290b1e9738Ssjur.brandeland@stericsson.com 		spin_lock_bh(&muxl->receive_lock);
130b482cd20SSjur Braendeland 		up = get_from_id(&muxl->srvl_list, id);
1310b1e9738Ssjur.brandeland@stericsson.com 		rcu_assign_pointer(muxl->up_cache[idx], up);
1320b1e9738Ssjur.brandeland@stericsson.com 		spin_unlock_bh(&muxl->receive_lock);
133b482cd20SSjur Braendeland 	}
134b482cd20SSjur Braendeland 	return up;
135b482cd20SSjur Braendeland }
136b482cd20SSjur Braendeland 
get_dn(struct cfmuxl * muxl,struct dev_info * dev_info)137b482cd20SSjur Braendeland static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)
138b482cd20SSjur Braendeland {
139b482cd20SSjur Braendeland 	struct cflayer *dn;
140b482cd20SSjur Braendeland 	int idx = dev_info->id % DN_CACHE_SIZE;
1410b1e9738Ssjur.brandeland@stericsson.com 	dn = rcu_dereference(muxl->dn_cache[idx]);
142b482cd20SSjur Braendeland 	if (dn == NULL || dn->id != dev_info->id) {
1430b1e9738Ssjur.brandeland@stericsson.com 		spin_lock_bh(&muxl->transmit_lock);
144b482cd20SSjur Braendeland 		dn = get_from_id(&muxl->frml_list, dev_info->id);
1450b1e9738Ssjur.brandeland@stericsson.com 		rcu_assign_pointer(muxl->dn_cache[idx], dn);
1460b1e9738Ssjur.brandeland@stericsson.com 		spin_unlock_bh(&muxl->transmit_lock);
147b482cd20SSjur Braendeland 	}
148b482cd20SSjur Braendeland 	return dn;
149b482cd20SSjur Braendeland }
150b482cd20SSjur Braendeland 
cfmuxl_remove_uplayer(struct cflayer * layr,u8 id)151b482cd20SSjur Braendeland struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
152b482cd20SSjur Braendeland {
153b482cd20SSjur Braendeland 	struct cflayer *up;
154b482cd20SSjur Braendeland 	struct cfmuxl *muxl = container_obj(layr);
1550b1e9738Ssjur.brandeland@stericsson.com 	int idx = id % UP_CACHE_SIZE;
1560b1e9738Ssjur.brandeland@stericsson.com 
15754e90fb5Ssjur.brandeland@stericsson.com 	if (id == 0) {
15854e90fb5Ssjur.brandeland@stericsson.com 		pr_warn("Trying to remove control layer\n");
15954e90fb5Ssjur.brandeland@stericsson.com 		return NULL;
16054e90fb5Ssjur.brandeland@stericsson.com 	}
16154e90fb5Ssjur.brandeland@stericsson.com 
1620b1e9738Ssjur.brandeland@stericsson.com 	spin_lock_bh(&muxl->receive_lock);
1630b1e9738Ssjur.brandeland@stericsson.com 	up = get_from_id(&muxl->srvl_list, id);
1645b208656SSjur Braendeland 	if (up == NULL)
165a9a8f107SSjur Braendeland 		goto out;
1660b1e9738Ssjur.brandeland@stericsson.com 
167a9b3cd7fSStephen Hemminger 	RCU_INIT_POINTER(muxl->up_cache[idx], NULL);
1680b1e9738Ssjur.brandeland@stericsson.com 	list_del_rcu(&up->node);
169a9a8f107SSjur Braendeland out:
1700b1e9738Ssjur.brandeland@stericsson.com 	spin_unlock_bh(&muxl->receive_lock);
171b482cd20SSjur Braendeland 	return up;
172b482cd20SSjur Braendeland }
173b482cd20SSjur Braendeland 
cfmuxl_receive(struct cflayer * layr,struct cfpkt * pkt)174b482cd20SSjur Braendeland static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt)
175b482cd20SSjur Braendeland {
176b482cd20SSjur Braendeland 	int ret;
177b482cd20SSjur Braendeland 	struct cfmuxl *muxl = container_obj(layr);
178b482cd20SSjur Braendeland 	u8 id;
179b482cd20SSjur Braendeland 	struct cflayer *up;
180b482cd20SSjur Braendeland 	if (cfpkt_extr_head(pkt, &id, 1) < 0) {
181b31fa5baSJoe Perches 		pr_err("erroneous Caif Packet\n");
182b482cd20SSjur Braendeland 		cfpkt_destroy(pkt);
183b482cd20SSjur Braendeland 		return -EPROTO;
184b482cd20SSjur Braendeland 	}
1850b1e9738Ssjur.brandeland@stericsson.com 	rcu_read_lock();
186b482cd20SSjur Braendeland 	up = get_up(muxl, id);
1870b1e9738Ssjur.brandeland@stericsson.com 
188b482cd20SSjur Braendeland 	if (up == NULL) {
1890b1e9738Ssjur.brandeland@stericsson.com 		pr_debug("Received data on unknown link ID = %d (0x%x)"
1900b1e9738Ssjur.brandeland@stericsson.com 			" up == NULL", id, id);
191b482cd20SSjur Braendeland 		cfpkt_destroy(pkt);
192b482cd20SSjur Braendeland 		/*
193b482cd20SSjur Braendeland 		 * Don't return ERROR, since modem misbehaves and sends out
194b482cd20SSjur Braendeland 		 * flow on before linksetup response.
195b482cd20SSjur Braendeland 		 */
1960b1e9738Ssjur.brandeland@stericsson.com 
1970b1e9738Ssjur.brandeland@stericsson.com 		rcu_read_unlock();
198b482cd20SSjur Braendeland 		return /* CFGLU_EPROT; */ 0;
199b482cd20SSjur Braendeland 	}
2000b1e9738Ssjur.brandeland@stericsson.com 
2010b1e9738Ssjur.brandeland@stericsson.com 	/* We can't hold rcu_lock during receive, so take a ref count instead */
2025b208656SSjur Braendeland 	cfsrvl_get(up);
2030b1e9738Ssjur.brandeland@stericsson.com 	rcu_read_unlock();
2040b1e9738Ssjur.brandeland@stericsson.com 
205b482cd20SSjur Braendeland 	ret = up->receive(up, pkt);
2060b1e9738Ssjur.brandeland@stericsson.com 
2075b208656SSjur Braendeland 	cfsrvl_put(up);
208b482cd20SSjur Braendeland 	return ret;
209b482cd20SSjur Braendeland }
210b482cd20SSjur Braendeland 
cfmuxl_transmit(struct cflayer * layr,struct cfpkt * pkt)211b482cd20SSjur Braendeland static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt)
212b482cd20SSjur Braendeland {
213b482cd20SSjur Braendeland 	struct cfmuxl *muxl = container_obj(layr);
2140b1e9738Ssjur.brandeland@stericsson.com 	int err;
215b482cd20SSjur Braendeland 	u8 linkid;
216b482cd20SSjur Braendeland 	struct cflayer *dn;
217b482cd20SSjur Braendeland 	struct caif_payload_info *info = cfpkt_info(pkt);
21839b9afbbSSjur Brændeland 	BUG_ON(!info);
2190b1e9738Ssjur.brandeland@stericsson.com 
2200b1e9738Ssjur.brandeland@stericsson.com 	rcu_read_lock();
2210b1e9738Ssjur.brandeland@stericsson.com 
22239b9afbbSSjur Brændeland 	dn = get_dn(muxl, info->dev_info);
223b482cd20SSjur Braendeland 	if (dn == NULL) {
2240b1e9738Ssjur.brandeland@stericsson.com 		pr_debug("Send data on unknown phy ID = %d (0x%x)\n",
225b31fa5baSJoe Perches 			info->dev_info->id, info->dev_info->id);
2260b1e9738Ssjur.brandeland@stericsson.com 		rcu_read_unlock();
2270b1e9738Ssjur.brandeland@stericsson.com 		cfpkt_destroy(pkt);
228b482cd20SSjur Braendeland 		return -ENOTCONN;
229b482cd20SSjur Braendeland 	}
2300b1e9738Ssjur.brandeland@stericsson.com 
231b482cd20SSjur Braendeland 	info->hdr_len += 1;
232b482cd20SSjur Braendeland 	linkid = info->channel_id;
233b482cd20SSjur Braendeland 	cfpkt_add_head(pkt, &linkid, 1);
2340b1e9738Ssjur.brandeland@stericsson.com 
2350b1e9738Ssjur.brandeland@stericsson.com 	/* We can't hold rcu_lock during receive, so take a ref count instead */
2360b1e9738Ssjur.brandeland@stericsson.com 	cffrml_hold(dn);
2370b1e9738Ssjur.brandeland@stericsson.com 
2380b1e9738Ssjur.brandeland@stericsson.com 	rcu_read_unlock();
2390b1e9738Ssjur.brandeland@stericsson.com 
2400b1e9738Ssjur.brandeland@stericsson.com 	err = dn->transmit(dn, pkt);
2410b1e9738Ssjur.brandeland@stericsson.com 
2420b1e9738Ssjur.brandeland@stericsson.com 	cffrml_put(dn);
2430b1e9738Ssjur.brandeland@stericsson.com 	return err;
244b482cd20SSjur Braendeland }
245b482cd20SSjur Braendeland 
cfmuxl_ctrlcmd(struct cflayer * layr,enum caif_ctrlcmd ctrl,int phyid)246b482cd20SSjur Braendeland static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
247b482cd20SSjur Braendeland 			   int phyid)
248b482cd20SSjur Braendeland {
249b482cd20SSjur Braendeland 	struct cfmuxl *muxl = container_obj(layr);
250b482cd20SSjur Braendeland 	struct cflayer *layer;
2510b1e9738Ssjur.brandeland@stericsson.com 
2520b1e9738Ssjur.brandeland@stericsson.com 	rcu_read_lock();
2530b1e9738Ssjur.brandeland@stericsson.com 	list_for_each_entry_rcu(layer, &muxl->srvl_list, node) {
25454e90fb5Ssjur.brandeland@stericsson.com 
25554e90fb5Ssjur.brandeland@stericsson.com 		if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) {
25654e90fb5Ssjur.brandeland@stericsson.com 
257a1b7f85eSsjur.brandeland@stericsson.com 			if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND ||
25854e90fb5Ssjur.brandeland@stericsson.com 				ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) &&
259b01377a4Ssjur.brandeland@stericsson.com 					layer->id != 0)
260b01377a4Ssjur.brandeland@stericsson.com 				cfmuxl_remove_uplayer(layr, layer->id);
26154e90fb5Ssjur.brandeland@stericsson.com 
2620b1e9738Ssjur.brandeland@stericsson.com 			/* NOTE: ctrlcmd is not allowed to block */
263b482cd20SSjur Braendeland 			layer->ctrlcmd(layer, ctrl, phyid);
264b482cd20SSjur Braendeland 		}
26554e90fb5Ssjur.brandeland@stericsson.com 	}
2660b1e9738Ssjur.brandeland@stericsson.com 	rcu_read_unlock();
267b482cd20SSjur Braendeland }
268