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