162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2010 462306a36Sopenharmony_ci * Author: Sjur Brendeland 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/stddef.h> 1062306a36Sopenharmony_ci#include <linux/spinlock.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/rculist.h> 1362306a36Sopenharmony_ci#include <net/caif/cfpkt.h> 1462306a36Sopenharmony_ci#include <net/caif/cfmuxl.h> 1562306a36Sopenharmony_ci#include <net/caif/cfsrvl.h> 1662306a36Sopenharmony_ci#include <net/caif/cffrml.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define container_obj(layr) container_of(layr, struct cfmuxl, layer) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define CAIF_CTRL_CHANNEL 0 2162306a36Sopenharmony_ci#define UP_CACHE_SIZE 8 2262306a36Sopenharmony_ci#define DN_CACHE_SIZE 8 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct cfmuxl { 2562306a36Sopenharmony_ci struct cflayer layer; 2662306a36Sopenharmony_ci struct list_head srvl_list; 2762306a36Sopenharmony_ci struct list_head frml_list; 2862306a36Sopenharmony_ci struct cflayer *up_cache[UP_CACHE_SIZE]; 2962306a36Sopenharmony_ci struct cflayer *dn_cache[DN_CACHE_SIZE]; 3062306a36Sopenharmony_ci /* 3162306a36Sopenharmony_ci * Set when inserting or removing downwards layers. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci spinlock_t transmit_lock; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* 3662306a36Sopenharmony_ci * Set when inserting or removing upwards layers. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci spinlock_t receive_lock; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt); 4362306a36Sopenharmony_cistatic int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt); 4462306a36Sopenharmony_cistatic void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 4562306a36Sopenharmony_ci int phyid); 4662306a36Sopenharmony_cistatic struct cflayer *get_up(struct cfmuxl *muxl, u16 id); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistruct cflayer *cfmuxl_create(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct cfmuxl *this = kzalloc(sizeof(struct cfmuxl), GFP_ATOMIC); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (!this) 5362306a36Sopenharmony_ci return NULL; 5462306a36Sopenharmony_ci this->layer.receive = cfmuxl_receive; 5562306a36Sopenharmony_ci this->layer.transmit = cfmuxl_transmit; 5662306a36Sopenharmony_ci this->layer.ctrlcmd = cfmuxl_ctrlcmd; 5762306a36Sopenharmony_ci INIT_LIST_HEAD(&this->srvl_list); 5862306a36Sopenharmony_ci INIT_LIST_HEAD(&this->frml_list); 5962306a36Sopenharmony_ci spin_lock_init(&this->transmit_lock); 6062306a36Sopenharmony_ci spin_lock_init(&this->receive_lock); 6162306a36Sopenharmony_ci snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux"); 6262306a36Sopenharmony_ci return &this->layer; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ciint cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct cfmuxl *muxl = (struct cfmuxl *) layr; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci spin_lock_bh(&muxl->transmit_lock); 7062306a36Sopenharmony_ci list_add_rcu(&dn->node, &muxl->frml_list); 7162306a36Sopenharmony_ci spin_unlock_bh(&muxl->transmit_lock); 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic struct cflayer *get_from_id(struct list_head *list, u16 id) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct cflayer *lyr; 7862306a36Sopenharmony_ci list_for_each_entry_rcu(lyr, list, node) { 7962306a36Sopenharmony_ci if (lyr->id == id) 8062306a36Sopenharmony_ci return lyr; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return NULL; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ciint cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 8962306a36Sopenharmony_ci struct cflayer *old; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci spin_lock_bh(&muxl->receive_lock); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Two entries with same id is wrong, so remove old layer from mux */ 9462306a36Sopenharmony_ci old = get_from_id(&muxl->srvl_list, linkid); 9562306a36Sopenharmony_ci if (old != NULL) 9662306a36Sopenharmony_ci list_del_rcu(&old->node); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci list_add_rcu(&up->node, &muxl->srvl_list); 9962306a36Sopenharmony_ci spin_unlock_bh(&muxl->receive_lock); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistruct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 10762306a36Sopenharmony_ci struct cflayer *dn; 10862306a36Sopenharmony_ci int idx = phyid % DN_CACHE_SIZE; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci spin_lock_bh(&muxl->transmit_lock); 11162306a36Sopenharmony_ci RCU_INIT_POINTER(muxl->dn_cache[idx], NULL); 11262306a36Sopenharmony_ci dn = get_from_id(&muxl->frml_list, phyid); 11362306a36Sopenharmony_ci if (dn == NULL) 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci list_del_rcu(&dn->node); 11762306a36Sopenharmony_ci caif_assert(dn != NULL); 11862306a36Sopenharmony_ciout: 11962306a36Sopenharmony_ci spin_unlock_bh(&muxl->transmit_lock); 12062306a36Sopenharmony_ci return dn; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic struct cflayer *get_up(struct cfmuxl *muxl, u16 id) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct cflayer *up; 12662306a36Sopenharmony_ci int idx = id % UP_CACHE_SIZE; 12762306a36Sopenharmony_ci up = rcu_dereference(muxl->up_cache[idx]); 12862306a36Sopenharmony_ci if (up == NULL || up->id != id) { 12962306a36Sopenharmony_ci spin_lock_bh(&muxl->receive_lock); 13062306a36Sopenharmony_ci up = get_from_id(&muxl->srvl_list, id); 13162306a36Sopenharmony_ci rcu_assign_pointer(muxl->up_cache[idx], up); 13262306a36Sopenharmony_ci spin_unlock_bh(&muxl->receive_lock); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci return up; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct cflayer *dn; 14062306a36Sopenharmony_ci int idx = dev_info->id % DN_CACHE_SIZE; 14162306a36Sopenharmony_ci dn = rcu_dereference(muxl->dn_cache[idx]); 14262306a36Sopenharmony_ci if (dn == NULL || dn->id != dev_info->id) { 14362306a36Sopenharmony_ci spin_lock_bh(&muxl->transmit_lock); 14462306a36Sopenharmony_ci dn = get_from_id(&muxl->frml_list, dev_info->id); 14562306a36Sopenharmony_ci rcu_assign_pointer(muxl->dn_cache[idx], dn); 14662306a36Sopenharmony_ci spin_unlock_bh(&muxl->transmit_lock); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci return dn; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistruct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct cflayer *up; 15462306a36Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 15562306a36Sopenharmony_ci int idx = id % UP_CACHE_SIZE; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (id == 0) { 15862306a36Sopenharmony_ci pr_warn("Trying to remove control layer\n"); 15962306a36Sopenharmony_ci return NULL; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_lock_bh(&muxl->receive_lock); 16362306a36Sopenharmony_ci up = get_from_id(&muxl->srvl_list, id); 16462306a36Sopenharmony_ci if (up == NULL) 16562306a36Sopenharmony_ci goto out; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci RCU_INIT_POINTER(muxl->up_cache[idx], NULL); 16862306a36Sopenharmony_ci list_del_rcu(&up->node); 16962306a36Sopenharmony_ciout: 17062306a36Sopenharmony_ci spin_unlock_bh(&muxl->receive_lock); 17162306a36Sopenharmony_ci return up; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci int ret; 17762306a36Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 17862306a36Sopenharmony_ci u8 id; 17962306a36Sopenharmony_ci struct cflayer *up; 18062306a36Sopenharmony_ci if (cfpkt_extr_head(pkt, &id, 1) < 0) { 18162306a36Sopenharmony_ci pr_err("erroneous Caif Packet\n"); 18262306a36Sopenharmony_ci cfpkt_destroy(pkt); 18362306a36Sopenharmony_ci return -EPROTO; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci rcu_read_lock(); 18662306a36Sopenharmony_ci up = get_up(muxl, id); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (up == NULL) { 18962306a36Sopenharmony_ci pr_debug("Received data on unknown link ID = %d (0x%x)" 19062306a36Sopenharmony_ci " up == NULL", id, id); 19162306a36Sopenharmony_ci cfpkt_destroy(pkt); 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * Don't return ERROR, since modem misbehaves and sends out 19462306a36Sopenharmony_ci * flow on before linksetup response. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci rcu_read_unlock(); 19862306a36Sopenharmony_ci return /* CFGLU_EPROT; */ 0; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* We can't hold rcu_lock during receive, so take a ref count instead */ 20262306a36Sopenharmony_ci cfsrvl_get(up); 20362306a36Sopenharmony_ci rcu_read_unlock(); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ret = up->receive(up, pkt); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci cfsrvl_put(up); 20862306a36Sopenharmony_ci return ret; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 21462306a36Sopenharmony_ci int err; 21562306a36Sopenharmony_ci u8 linkid; 21662306a36Sopenharmony_ci struct cflayer *dn; 21762306a36Sopenharmony_ci struct caif_payload_info *info = cfpkt_info(pkt); 21862306a36Sopenharmony_ci BUG_ON(!info); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci rcu_read_lock(); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci dn = get_dn(muxl, info->dev_info); 22362306a36Sopenharmony_ci if (dn == NULL) { 22462306a36Sopenharmony_ci pr_debug("Send data on unknown phy ID = %d (0x%x)\n", 22562306a36Sopenharmony_ci info->dev_info->id, info->dev_info->id); 22662306a36Sopenharmony_ci rcu_read_unlock(); 22762306a36Sopenharmony_ci cfpkt_destroy(pkt); 22862306a36Sopenharmony_ci return -ENOTCONN; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci info->hdr_len += 1; 23262306a36Sopenharmony_ci linkid = info->channel_id; 23362306a36Sopenharmony_ci cfpkt_add_head(pkt, &linkid, 1); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* We can't hold rcu_lock during receive, so take a ref count instead */ 23662306a36Sopenharmony_ci cffrml_hold(dn); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci rcu_read_unlock(); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci err = dn->transmit(dn, pkt); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci cffrml_put(dn); 24362306a36Sopenharmony_ci return err; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl, 24762306a36Sopenharmony_ci int phyid) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct cfmuxl *muxl = container_obj(layr); 25062306a36Sopenharmony_ci struct cflayer *layer; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci rcu_read_lock(); 25362306a36Sopenharmony_ci list_for_each_entry_rcu(layer, &muxl->srvl_list, node) { 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) { 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND || 25862306a36Sopenharmony_ci ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) && 25962306a36Sopenharmony_ci layer->id != 0) 26062306a36Sopenharmony_ci cfmuxl_remove_uplayer(layr, layer->id); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* NOTE: ctrlcmd is not allowed to block */ 26362306a36Sopenharmony_ci layer->ctrlcmd(layer, ctrl, phyid); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci rcu_read_unlock(); 26762306a36Sopenharmony_ci} 268