18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 48c2ecf20Sopenharmony_ci * Author: Sjur Brendeland 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/stddef.h> 108c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/rculist.h> 138c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h> 148c2ecf20Sopenharmony_ci#include <net/caif/cfmuxl.h> 158c2ecf20Sopenharmony_ci#include <net/caif/cfsrvl.h> 168c2ecf20Sopenharmony_ci#include <net/caif/cffrml.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define container_obj(layr) container_of(layr, struct cfmuxl, layer) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define CAIF_CTRL_CHANNEL 0 218c2ecf20Sopenharmony_ci#define UP_CACHE_SIZE 8 228c2ecf20Sopenharmony_ci#define DN_CACHE_SIZE 8 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct cfmuxl { 258c2ecf20Sopenharmony_ci struct cflayer layer; 268c2ecf20Sopenharmony_ci struct list_head srvl_list; 278c2ecf20Sopenharmony_ci struct list_head frml_list; 288c2ecf20Sopenharmony_ci struct cflayer *up_cache[UP_CACHE_SIZE]; 298c2ecf20Sopenharmony_ci struct cflayer *dn_cache[DN_CACHE_SIZE]; 308c2ecf20Sopenharmony_ci /* 318c2ecf20Sopenharmony_ci * Set when inserting or removing downwards layers. 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci spinlock_t transmit_lock; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* 368c2ecf20Sopenharmony_ci * Set when inserting or removing upwards layers. 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ci spinlock_t receive_lock; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt); 438c2ecf20Sopenharmony_cistatic int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt); 448c2ecf20Sopenharmony_cistatic void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 458c2ecf20Sopenharmony_ci int phyid); 468c2ecf20Sopenharmony_cistatic struct cflayer *get_up(struct cfmuxl *muxl, u16 id); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct cflayer *cfmuxl_create(void) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct cfmuxl *this = kzalloc(sizeof(struct cfmuxl), GFP_ATOMIC); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!this) 538c2ecf20Sopenharmony_ci return NULL; 548c2ecf20Sopenharmony_ci this->layer.receive = cfmuxl_receive; 558c2ecf20Sopenharmony_ci this->layer.transmit = cfmuxl_transmit; 568c2ecf20Sopenharmony_ci this->layer.ctrlcmd = cfmuxl_ctrlcmd; 578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&this->srvl_list); 588c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&this->frml_list); 598c2ecf20Sopenharmony_ci spin_lock_init(&this->transmit_lock); 608c2ecf20Sopenharmony_ci spin_lock_init(&this->receive_lock); 618c2ecf20Sopenharmony_ci snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux"); 628c2ecf20Sopenharmony_ci return &this->layer; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ciint cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct cfmuxl *muxl = (struct cfmuxl *) layr; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci spin_lock_bh(&muxl->transmit_lock); 708c2ecf20Sopenharmony_ci list_add_rcu(&dn->node, &muxl->frml_list); 718c2ecf20Sopenharmony_ci spin_unlock_bh(&muxl->transmit_lock); 728c2ecf20Sopenharmony_ci return 0; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic struct cflayer *get_from_id(struct list_head *list, u16 id) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct cflayer *lyr; 788c2ecf20Sopenharmony_ci list_for_each_entry_rcu(lyr, list, node) { 798c2ecf20Sopenharmony_ci if (lyr->id == id) 808c2ecf20Sopenharmony_ci return lyr; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return NULL; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciint cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 898c2ecf20Sopenharmony_ci struct cflayer *old; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci spin_lock_bh(&muxl->receive_lock); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Two entries with same id is wrong, so remove old layer from mux */ 948c2ecf20Sopenharmony_ci old = get_from_id(&muxl->srvl_list, linkid); 958c2ecf20Sopenharmony_ci if (old != NULL) 968c2ecf20Sopenharmony_ci list_del_rcu(&old->node); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci list_add_rcu(&up->node, &muxl->srvl_list); 998c2ecf20Sopenharmony_ci spin_unlock_bh(&muxl->receive_lock); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistruct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 1078c2ecf20Sopenharmony_ci struct cflayer *dn; 1088c2ecf20Sopenharmony_ci int idx = phyid % DN_CACHE_SIZE; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci spin_lock_bh(&muxl->transmit_lock); 1118c2ecf20Sopenharmony_ci RCU_INIT_POINTER(muxl->dn_cache[idx], NULL); 1128c2ecf20Sopenharmony_ci dn = get_from_id(&muxl->frml_list, phyid); 1138c2ecf20Sopenharmony_ci if (dn == NULL) 1148c2ecf20Sopenharmony_ci goto out; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci list_del_rcu(&dn->node); 1178c2ecf20Sopenharmony_ci caif_assert(dn != NULL); 1188c2ecf20Sopenharmony_ciout: 1198c2ecf20Sopenharmony_ci spin_unlock_bh(&muxl->transmit_lock); 1208c2ecf20Sopenharmony_ci return dn; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic struct cflayer *get_up(struct cfmuxl *muxl, u16 id) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct cflayer *up; 1268c2ecf20Sopenharmony_ci int idx = id % UP_CACHE_SIZE; 1278c2ecf20Sopenharmony_ci up = rcu_dereference(muxl->up_cache[idx]); 1288c2ecf20Sopenharmony_ci if (up == NULL || up->id != id) { 1298c2ecf20Sopenharmony_ci spin_lock_bh(&muxl->receive_lock); 1308c2ecf20Sopenharmony_ci up = get_from_id(&muxl->srvl_list, id); 1318c2ecf20Sopenharmony_ci rcu_assign_pointer(muxl->up_cache[idx], up); 1328c2ecf20Sopenharmony_ci spin_unlock_bh(&muxl->receive_lock); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci return up; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct cflayer *dn; 1408c2ecf20Sopenharmony_ci int idx = dev_info->id % DN_CACHE_SIZE; 1418c2ecf20Sopenharmony_ci dn = rcu_dereference(muxl->dn_cache[idx]); 1428c2ecf20Sopenharmony_ci if (dn == NULL || dn->id != dev_info->id) { 1438c2ecf20Sopenharmony_ci spin_lock_bh(&muxl->transmit_lock); 1448c2ecf20Sopenharmony_ci dn = get_from_id(&muxl->frml_list, dev_info->id); 1458c2ecf20Sopenharmony_ci rcu_assign_pointer(muxl->dn_cache[idx], dn); 1468c2ecf20Sopenharmony_ci spin_unlock_bh(&muxl->transmit_lock); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci return dn; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistruct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct cflayer *up; 1548c2ecf20Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 1558c2ecf20Sopenharmony_ci int idx = id % UP_CACHE_SIZE; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (id == 0) { 1588c2ecf20Sopenharmony_ci pr_warn("Trying to remove control layer\n"); 1598c2ecf20Sopenharmony_ci return NULL; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci spin_lock_bh(&muxl->receive_lock); 1638c2ecf20Sopenharmony_ci up = get_from_id(&muxl->srvl_list, id); 1648c2ecf20Sopenharmony_ci if (up == NULL) 1658c2ecf20Sopenharmony_ci goto out; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci RCU_INIT_POINTER(muxl->up_cache[idx], NULL); 1688c2ecf20Sopenharmony_ci list_del_rcu(&up->node); 1698c2ecf20Sopenharmony_ciout: 1708c2ecf20Sopenharmony_ci spin_unlock_bh(&muxl->receive_lock); 1718c2ecf20Sopenharmony_ci return up; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 1788c2ecf20Sopenharmony_ci u8 id; 1798c2ecf20Sopenharmony_ci struct cflayer *up; 1808c2ecf20Sopenharmony_ci if (cfpkt_extr_head(pkt, &id, 1) < 0) { 1818c2ecf20Sopenharmony_ci pr_err("erroneous Caif Packet\n"); 1828c2ecf20Sopenharmony_ci cfpkt_destroy(pkt); 1838c2ecf20Sopenharmony_ci return -EPROTO; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci rcu_read_lock(); 1868c2ecf20Sopenharmony_ci up = get_up(muxl, id); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (up == NULL) { 1898c2ecf20Sopenharmony_ci pr_debug("Received data on unknown link ID = %d (0x%x)" 1908c2ecf20Sopenharmony_ci " up == NULL", id, id); 1918c2ecf20Sopenharmony_ci cfpkt_destroy(pkt); 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * Don't return ERROR, since modem misbehaves and sends out 1948c2ecf20Sopenharmony_ci * flow on before linksetup response. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rcu_read_unlock(); 1988c2ecf20Sopenharmony_ci return /* CFGLU_EPROT; */ 0; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* We can't hold rcu_lock during receive, so take a ref count instead */ 2028c2ecf20Sopenharmony_ci cfsrvl_get(up); 2038c2ecf20Sopenharmony_ci rcu_read_unlock(); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ret = up->receive(up, pkt); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci cfsrvl_put(up); 2088c2ecf20Sopenharmony_ci return ret; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 2148c2ecf20Sopenharmony_ci int err; 2158c2ecf20Sopenharmony_ci u8 linkid; 2168c2ecf20Sopenharmony_ci struct cflayer *dn; 2178c2ecf20Sopenharmony_ci struct caif_payload_info *info = cfpkt_info(pkt); 2188c2ecf20Sopenharmony_ci BUG_ON(!info); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci rcu_read_lock(); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci dn = get_dn(muxl, info->dev_info); 2238c2ecf20Sopenharmony_ci if (dn == NULL) { 2248c2ecf20Sopenharmony_ci pr_debug("Send data on unknown phy ID = %d (0x%x)\n", 2258c2ecf20Sopenharmony_ci info->dev_info->id, info->dev_info->id); 2268c2ecf20Sopenharmony_ci rcu_read_unlock(); 2278c2ecf20Sopenharmony_ci cfpkt_destroy(pkt); 2288c2ecf20Sopenharmony_ci return -ENOTCONN; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci info->hdr_len += 1; 2328c2ecf20Sopenharmony_ci linkid = info->channel_id; 2338c2ecf20Sopenharmony_ci cfpkt_add_head(pkt, &linkid, 1); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* We can't hold rcu_lock during receive, so take a ref count instead */ 2368c2ecf20Sopenharmony_ci cffrml_hold(dn); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci rcu_read_unlock(); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci err = dn->transmit(dn, pkt); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci cffrml_put(dn); 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 2478c2ecf20Sopenharmony_ci int phyid) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 2508c2ecf20Sopenharmony_ci struct cflayer *layer; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci rcu_read_lock(); 2538c2ecf20Sopenharmony_ci list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) { 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND || 2588c2ecf20Sopenharmony_ci ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && 2598c2ecf20Sopenharmony_ci layer->id != 0) 2608c2ecf20Sopenharmony_ci cfmuxl_remove_uplayer(layr, layer->id); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* NOTE: ctrlcmd is not allowed to block */ 2638c2ecf20Sopenharmony_ci layer->ctrlcmd(layer, ctrl, phyid); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci rcu_read_unlock(); 2678c2ecf20Sopenharmony_ci} 268