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/kernel.h> 1062306a36Sopenharmony_ci#include <linux/stddef.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <net/caif/caif_layer.h> 1562306a36Sopenharmony_ci#include <net/caif/cfpkt.h> 1662306a36Sopenharmony_ci#include <net/caif/cfcnfg.h> 1762306a36Sopenharmony_ci#include <net/caif/cfctrl.h> 1862306a36Sopenharmony_ci#include <net/caif/cfmuxl.h> 1962306a36Sopenharmony_ci#include <net/caif/cffrml.h> 2062306a36Sopenharmony_ci#include <net/caif/cfserl.h> 2162306a36Sopenharmony_ci#include <net/caif/cfsrvl.h> 2262306a36Sopenharmony_ci#include <net/caif/caif_dev.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define container_obj(layr) container_of(layr, struct cfcnfg, layer) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Information about CAIF physical interfaces held by Config Module in order 2762306a36Sopenharmony_ci * to manage physical interfaces 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_cistruct cfcnfg_phyinfo { 3062306a36Sopenharmony_ci struct list_head node; 3162306a36Sopenharmony_ci bool up; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* Pointer to the layer below the MUX (framing layer) */ 3462306a36Sopenharmony_ci struct cflayer *frm_layer; 3562306a36Sopenharmony_ci /* Pointer to the lowest actual physical layer */ 3662306a36Sopenharmony_ci struct cflayer *phy_layer; 3762306a36Sopenharmony_ci /* Unique identifier of the physical interface */ 3862306a36Sopenharmony_ci unsigned int id; 3962306a36Sopenharmony_ci /* Preference of the physical in interface */ 4062306a36Sopenharmony_ci enum cfcnfg_phy_preference pref; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Information about the physical device */ 4362306a36Sopenharmony_ci struct dev_info dev_info; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* Interface index */ 4662306a36Sopenharmony_ci int ifindex; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* Protocol head room added for CAIF link layer */ 4962306a36Sopenharmony_ci int head_room; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Use Start of frame checksum */ 5262306a36Sopenharmony_ci bool use_fcs; 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistruct cfcnfg { 5662306a36Sopenharmony_ci struct cflayer layer; 5762306a36Sopenharmony_ci struct cflayer *ctrl; 5862306a36Sopenharmony_ci struct cflayer *mux; 5962306a36Sopenharmony_ci struct list_head phys; 6062306a36Sopenharmony_ci struct mutex lock; 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, 6462306a36Sopenharmony_ci enum cfctrl_srv serv, u8 phyid, 6562306a36Sopenharmony_ci struct cflayer *adapt_layer); 6662306a36Sopenharmony_cistatic void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id); 6762306a36Sopenharmony_cistatic void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, 6862306a36Sopenharmony_ci struct cflayer *adapt_layer); 6962306a36Sopenharmony_cistatic void cfctrl_resp_func(void); 7062306a36Sopenharmony_cistatic void cfctrl_enum_resp(void); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct cfcnfg *cfcnfg_create(void) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct cfcnfg *this; 7562306a36Sopenharmony_ci struct cfctrl_rsp *resp; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci might_sleep(); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Initiate this layer */ 8062306a36Sopenharmony_ci this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); 8162306a36Sopenharmony_ci if (!this) 8262306a36Sopenharmony_ci return NULL; 8362306a36Sopenharmony_ci this->mux = cfmuxl_create(); 8462306a36Sopenharmony_ci if (!this->mux) 8562306a36Sopenharmony_ci goto out_of_mem; 8662306a36Sopenharmony_ci this->ctrl = cfctrl_create(); 8762306a36Sopenharmony_ci if (!this->ctrl) 8862306a36Sopenharmony_ci goto out_of_mem; 8962306a36Sopenharmony_ci /* Initiate response functions */ 9062306a36Sopenharmony_ci resp = cfctrl_get_respfuncs(this->ctrl); 9162306a36Sopenharmony_ci resp->enum_rsp = cfctrl_enum_resp; 9262306a36Sopenharmony_ci resp->linkerror_ind = cfctrl_resp_func; 9362306a36Sopenharmony_ci resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp; 9462306a36Sopenharmony_ci resp->sleep_rsp = cfctrl_resp_func; 9562306a36Sopenharmony_ci resp->wake_rsp = cfctrl_resp_func; 9662306a36Sopenharmony_ci resp->restart_rsp = cfctrl_resp_func; 9762306a36Sopenharmony_ci resp->radioset_rsp = cfctrl_resp_func; 9862306a36Sopenharmony_ci resp->linksetup_rsp = cfcnfg_linkup_rsp; 9962306a36Sopenharmony_ci resp->reject_rsp = cfcnfg_reject_rsp; 10062306a36Sopenharmony_ci INIT_LIST_HEAD(&this->phys); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci cfmuxl_set_uplayer(this->mux, this->ctrl, 0); 10362306a36Sopenharmony_ci layer_set_dn(this->ctrl, this->mux); 10462306a36Sopenharmony_ci layer_set_up(this->ctrl, this); 10562306a36Sopenharmony_ci mutex_init(&this->lock); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return this; 10862306a36Sopenharmony_ciout_of_mem: 10962306a36Sopenharmony_ci synchronize_rcu(); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci kfree(this->mux); 11262306a36Sopenharmony_ci kfree(this->ctrl); 11362306a36Sopenharmony_ci kfree(this); 11462306a36Sopenharmony_ci return NULL; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_civoid cfcnfg_remove(struct cfcnfg *cfg) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci might_sleep(); 12062306a36Sopenharmony_ci if (cfg) { 12162306a36Sopenharmony_ci synchronize_rcu(); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci kfree(cfg->mux); 12462306a36Sopenharmony_ci cfctrl_remove(cfg->ctrl); 12562306a36Sopenharmony_ci kfree(cfg); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void cfctrl_resp_func(void) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, 13462306a36Sopenharmony_ci u8 phyid) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct cfcnfg_phyinfo *phy; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) 13962306a36Sopenharmony_ci if (phy->id == phyid) 14062306a36Sopenharmony_ci return phy; 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void cfctrl_enum_resp(void) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, 14962306a36Sopenharmony_ci enum cfcnfg_phy_preference phy_pref) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci /* Try to match with specified preference */ 15262306a36Sopenharmony_ci struct cfcnfg_phyinfo *phy; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) { 15562306a36Sopenharmony_ci if (phy->up && phy->pref == phy_pref && 15662306a36Sopenharmony_ci phy->frm_layer != NULL) 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return &phy->dev_info; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Otherwise just return something */ 16262306a36Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) 16362306a36Sopenharmony_ci if (phy->up) 16462306a36Sopenharmony_ci return &phy->dev_info; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return NULL; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct cfcnfg_phyinfo *phy; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) 17462306a36Sopenharmony_ci if (phy->ifindex == ifi && phy->up) 17562306a36Sopenharmony_ci return phy->id; 17662306a36Sopenharmony_ci return -ENODEV; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciint caif_disconnect_client(struct net *net, struct cflayer *adap_layer) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u8 channel_id; 18262306a36Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(net); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci caif_assert(adap_layer != NULL); 18562306a36Sopenharmony_ci cfctrl_cancel_req(cfg->ctrl, adap_layer); 18662306a36Sopenharmony_ci channel_id = adap_layer->id; 18762306a36Sopenharmony_ci if (channel_id != 0) { 18862306a36Sopenharmony_ci struct cflayer *servl; 18962306a36Sopenharmony_ci servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); 19062306a36Sopenharmony_ci cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); 19162306a36Sopenharmony_ci if (servl != NULL) 19262306a36Sopenharmony_ci layer_set_up(servl, NULL); 19362306a36Sopenharmony_ci } else 19462306a36Sopenharmony_ci pr_debug("nothing to disconnect\n"); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* Do RCU sync before initiating cleanup */ 19762306a36Sopenharmony_ci synchronize_rcu(); 19862306a36Sopenharmony_ci if (adap_layer->ctrlcmd != NULL) 19962306a36Sopenharmony_ci adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ciEXPORT_SYMBOL(caif_disconnect_client); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_cistatic void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic const int protohead[CFCTRL_SRV_MASK] = { 21062306a36Sopenharmony_ci [CFCTRL_SRV_VEI] = 4, 21162306a36Sopenharmony_ci [CFCTRL_SRV_DATAGRAM] = 7, 21262306a36Sopenharmony_ci [CFCTRL_SRV_UTIL] = 4, 21362306a36Sopenharmony_ci [CFCTRL_SRV_RFM] = 3, 21462306a36Sopenharmony_ci [CFCTRL_SRV_DBG] = 3, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int caif_connect_req_to_link_param(struct cfcnfg *cnfg, 21962306a36Sopenharmony_ci struct caif_connect_request *s, 22062306a36Sopenharmony_ci struct cfctrl_link_param *l) 22162306a36Sopenharmony_ci{ 22262306a36Sopenharmony_ci struct dev_info *dev_info; 22362306a36Sopenharmony_ci enum cfcnfg_phy_preference pref; 22462306a36Sopenharmony_ci int res; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci memset(l, 0, sizeof(*l)); 22762306a36Sopenharmony_ci /* In caif protocol low value is high priority */ 22862306a36Sopenharmony_ci l->priority = CAIF_PRIO_MAX - s->priority + 1; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (s->ifindex != 0) { 23162306a36Sopenharmony_ci res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); 23262306a36Sopenharmony_ci if (res < 0) 23362306a36Sopenharmony_ci return res; 23462306a36Sopenharmony_ci l->phyid = res; 23562306a36Sopenharmony_ci } else { 23662306a36Sopenharmony_ci switch (s->link_selector) { 23762306a36Sopenharmony_ci case CAIF_LINK_HIGH_BANDW: 23862306a36Sopenharmony_ci pref = CFPHYPREF_HIGH_BW; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci case CAIF_LINK_LOW_LATENCY: 24162306a36Sopenharmony_ci pref = CFPHYPREF_LOW_LAT; 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci default: 24462306a36Sopenharmony_ci return -EINVAL; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci dev_info = cfcnfg_get_phyid(cnfg, pref); 24762306a36Sopenharmony_ci if (dev_info == NULL) 24862306a36Sopenharmony_ci return -ENODEV; 24962306a36Sopenharmony_ci l->phyid = dev_info->id; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci switch (s->protocol) { 25262306a36Sopenharmony_ci case CAIFPROTO_AT: 25362306a36Sopenharmony_ci l->linktype = CFCTRL_SRV_VEI; 25462306a36Sopenharmony_ci l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3; 25562306a36Sopenharmony_ci l->chtype = s->sockaddr.u.at.type & 0x3; 25662306a36Sopenharmony_ci break; 25762306a36Sopenharmony_ci case CAIFPROTO_DATAGRAM: 25862306a36Sopenharmony_ci l->linktype = CFCTRL_SRV_DATAGRAM; 25962306a36Sopenharmony_ci l->chtype = 0x00; 26062306a36Sopenharmony_ci l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; 26162306a36Sopenharmony_ci break; 26262306a36Sopenharmony_ci case CAIFPROTO_DATAGRAM_LOOP: 26362306a36Sopenharmony_ci l->linktype = CFCTRL_SRV_DATAGRAM; 26462306a36Sopenharmony_ci l->chtype = 0x03; 26562306a36Sopenharmony_ci l->endpoint = 0x00; 26662306a36Sopenharmony_ci l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci case CAIFPROTO_RFM: 26962306a36Sopenharmony_ci l->linktype = CFCTRL_SRV_RFM; 27062306a36Sopenharmony_ci l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; 27162306a36Sopenharmony_ci strscpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, 27262306a36Sopenharmony_ci sizeof(l->u.rfm.volume)); 27362306a36Sopenharmony_ci break; 27462306a36Sopenharmony_ci case CAIFPROTO_UTIL: 27562306a36Sopenharmony_ci l->linktype = CFCTRL_SRV_UTIL; 27662306a36Sopenharmony_ci l->endpoint = 0x00; 27762306a36Sopenharmony_ci l->chtype = 0x00; 27862306a36Sopenharmony_ci strscpy(l->u.utility.name, s->sockaddr.u.util.service, 27962306a36Sopenharmony_ci sizeof(l->u.utility.name)); 28062306a36Sopenharmony_ci caif_assert(sizeof(l->u.utility.name) > 10); 28162306a36Sopenharmony_ci l->u.utility.paramlen = s->param.size; 28262306a36Sopenharmony_ci if (l->u.utility.paramlen > sizeof(l->u.utility.params)) 28362306a36Sopenharmony_ci l->u.utility.paramlen = sizeof(l->u.utility.params); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci memcpy(l->u.utility.params, s->param.data, 28662306a36Sopenharmony_ci l->u.utility.paramlen); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci case CAIFPROTO_DEBUG: 29062306a36Sopenharmony_ci l->linktype = CFCTRL_SRV_DBG; 29162306a36Sopenharmony_ci l->endpoint = s->sockaddr.u.dbg.service; 29262306a36Sopenharmony_ci l->chtype = s->sockaddr.u.dbg.type; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci default: 29562306a36Sopenharmony_ci return -EINVAL; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ciint caif_connect_client(struct net *net, struct caif_connect_request *conn_req, 30162306a36Sopenharmony_ci struct cflayer *adap_layer, int *ifindex, 30262306a36Sopenharmony_ci int *proto_head, int *proto_tail) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct cflayer *frml; 30562306a36Sopenharmony_ci struct cfcnfg_phyinfo *phy; 30662306a36Sopenharmony_ci int err; 30762306a36Sopenharmony_ci struct cfctrl_link_param param; 30862306a36Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(net); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci rcu_read_lock(); 31162306a36Sopenharmony_ci err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); 31262306a36Sopenharmony_ci if (err) 31362306a36Sopenharmony_ci goto unlock; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid); 31662306a36Sopenharmony_ci if (!phy) { 31762306a36Sopenharmony_ci err = -ENODEV; 31862306a36Sopenharmony_ci goto unlock; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci err = -EINVAL; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (adap_layer == NULL) { 32362306a36Sopenharmony_ci pr_err("adap_layer is zero\n"); 32462306a36Sopenharmony_ci goto unlock; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci if (adap_layer->receive == NULL) { 32762306a36Sopenharmony_ci pr_err("adap_layer->receive is NULL\n"); 32862306a36Sopenharmony_ci goto unlock; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci if (adap_layer->ctrlcmd == NULL) { 33162306a36Sopenharmony_ci pr_err("adap_layer->ctrlcmd == NULL\n"); 33262306a36Sopenharmony_ci goto unlock; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci err = -ENODEV; 33662306a36Sopenharmony_ci frml = phy->frm_layer; 33762306a36Sopenharmony_ci if (frml == NULL) { 33862306a36Sopenharmony_ci pr_err("Specified PHY type does not exist!\n"); 33962306a36Sopenharmony_ci goto unlock; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci caif_assert(param.phyid == phy->id); 34262306a36Sopenharmony_ci caif_assert(phy->frm_layer->id == 34362306a36Sopenharmony_ci param.phyid); 34462306a36Sopenharmony_ci caif_assert(phy->phy_layer->id == 34562306a36Sopenharmony_ci param.phyid); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci *ifindex = phy->ifindex; 34862306a36Sopenharmony_ci *proto_tail = 2; 34962306a36Sopenharmony_ci *proto_head = protohead[param.linktype] + phy->head_room; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci rcu_read_unlock(); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ 35462306a36Sopenharmony_ci cfctrl_enum_req(cfg->ctrl, param.phyid); 35562306a36Sopenharmony_ci return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciunlock: 35862306a36Sopenharmony_ci rcu_read_unlock(); 35962306a36Sopenharmony_ci return err; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ciEXPORT_SYMBOL(caif_connect_client); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, 36462306a36Sopenharmony_ci struct cflayer *adapt_layer) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) 36762306a36Sopenharmony_ci adapt_layer->ctrlcmd(adapt_layer, 36862306a36Sopenharmony_ci CAIF_CTRLCMD_INIT_FAIL_RSP, 0); 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic void 37262306a36Sopenharmony_cicfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, 37362306a36Sopenharmony_ci u8 phyid, struct cflayer *adapt_layer) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct cfcnfg *cnfg = container_obj(layer); 37662306a36Sopenharmony_ci struct cflayer *servicel = NULL; 37762306a36Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo; 37862306a36Sopenharmony_ci struct net_device *netdev; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (channel_id == 0) { 38162306a36Sopenharmony_ci pr_warn("received channel_id zero\n"); 38262306a36Sopenharmony_ci if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) 38362306a36Sopenharmony_ci adapt_layer->ctrlcmd(adapt_layer, 38462306a36Sopenharmony_ci CAIF_CTRLCMD_INIT_FAIL_RSP, 0); 38562306a36Sopenharmony_ci return; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci rcu_read_lock(); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (adapt_layer == NULL) { 39162306a36Sopenharmony_ci pr_debug("link setup response but no client exist, send linkdown back\n"); 39262306a36Sopenharmony_ci cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); 39362306a36Sopenharmony_ci goto unlock; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci caif_assert(cnfg != NULL); 39762306a36Sopenharmony_ci caif_assert(phyid != 0); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); 40062306a36Sopenharmony_ci if (phyinfo == NULL) { 40162306a36Sopenharmony_ci pr_err("ERROR: Link Layer Device disappeared while connecting\n"); 40262306a36Sopenharmony_ci goto unlock; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci caif_assert(phyinfo != NULL); 40662306a36Sopenharmony_ci caif_assert(phyinfo->id == phyid); 40762306a36Sopenharmony_ci caif_assert(phyinfo->phy_layer != NULL); 40862306a36Sopenharmony_ci caif_assert(phyinfo->phy_layer->id == phyid); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci adapt_layer->id = channel_id; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci switch (serv) { 41362306a36Sopenharmony_ci case CFCTRL_SRV_VEI: 41462306a36Sopenharmony_ci servicel = cfvei_create(channel_id, &phyinfo->dev_info); 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case CFCTRL_SRV_DATAGRAM: 41762306a36Sopenharmony_ci servicel = cfdgml_create(channel_id, 41862306a36Sopenharmony_ci &phyinfo->dev_info); 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case CFCTRL_SRV_RFM: 42162306a36Sopenharmony_ci netdev = phyinfo->dev_info.dev; 42262306a36Sopenharmony_ci servicel = cfrfml_create(channel_id, &phyinfo->dev_info, 42362306a36Sopenharmony_ci netdev->mtu); 42462306a36Sopenharmony_ci break; 42562306a36Sopenharmony_ci case CFCTRL_SRV_UTIL: 42662306a36Sopenharmony_ci servicel = cfutill_create(channel_id, &phyinfo->dev_info); 42762306a36Sopenharmony_ci break; 42862306a36Sopenharmony_ci case CFCTRL_SRV_VIDEO: 42962306a36Sopenharmony_ci servicel = cfvidl_create(channel_id, &phyinfo->dev_info); 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci case CFCTRL_SRV_DBG: 43262306a36Sopenharmony_ci servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci default: 43562306a36Sopenharmony_ci pr_err("Protocol error. Link setup response - unknown channel type\n"); 43662306a36Sopenharmony_ci goto unlock; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci if (!servicel) 43962306a36Sopenharmony_ci goto unlock; 44062306a36Sopenharmony_ci layer_set_dn(servicel, cnfg->mux); 44162306a36Sopenharmony_ci cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); 44262306a36Sopenharmony_ci layer_set_up(servicel, adapt_layer); 44362306a36Sopenharmony_ci layer_set_dn(adapt_layer, servicel); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci rcu_read_unlock(); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); 44862306a36Sopenharmony_ci return; 44962306a36Sopenharmony_ciunlock: 45062306a36Sopenharmony_ci rcu_read_unlock(); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciint 45462306a36Sopenharmony_cicfcnfg_add_phy_layer(struct cfcnfg *cnfg, 45562306a36Sopenharmony_ci struct net_device *dev, struct cflayer *phy_layer, 45662306a36Sopenharmony_ci enum cfcnfg_phy_preference pref, 45762306a36Sopenharmony_ci struct cflayer *link_support, 45862306a36Sopenharmony_ci bool fcs, int head_room) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct cflayer *frml; 46162306a36Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo = NULL; 46262306a36Sopenharmony_ci int i, res = 0; 46362306a36Sopenharmony_ci u8 phyid; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci mutex_lock(&cnfg->lock); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* CAIF protocol allow maximum 6 link-layers */ 46862306a36Sopenharmony_ci for (i = 0; i < 7; i++) { 46962306a36Sopenharmony_ci phyid = (dev->ifindex + i) & 0x7; 47062306a36Sopenharmony_ci if (phyid == 0) 47162306a36Sopenharmony_ci continue; 47262306a36Sopenharmony_ci if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) 47362306a36Sopenharmony_ci goto got_phyid; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci pr_warn("Too many CAIF Link Layers (max 6)\n"); 47662306a36Sopenharmony_ci res = -EEXIST; 47762306a36Sopenharmony_ci goto out; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_cigot_phyid: 48062306a36Sopenharmony_ci phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC); 48162306a36Sopenharmony_ci if (!phyinfo) { 48262306a36Sopenharmony_ci res = -ENOMEM; 48362306a36Sopenharmony_ci goto out; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci phy_layer->id = phyid; 48762306a36Sopenharmony_ci phyinfo->pref = pref; 48862306a36Sopenharmony_ci phyinfo->id = phyid; 48962306a36Sopenharmony_ci phyinfo->dev_info.id = phyid; 49062306a36Sopenharmony_ci phyinfo->dev_info.dev = dev; 49162306a36Sopenharmony_ci phyinfo->phy_layer = phy_layer; 49262306a36Sopenharmony_ci phyinfo->ifindex = dev->ifindex; 49362306a36Sopenharmony_ci phyinfo->head_room = head_room; 49462306a36Sopenharmony_ci phyinfo->use_fcs = fcs; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci frml = cffrml_create(phyid, fcs); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (!frml) { 49962306a36Sopenharmony_ci res = -ENOMEM; 50062306a36Sopenharmony_ci goto out_err; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci phyinfo->frm_layer = frml; 50362306a36Sopenharmony_ci layer_set_up(frml, cnfg->mux); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (link_support != NULL) { 50662306a36Sopenharmony_ci link_support->id = phyid; 50762306a36Sopenharmony_ci layer_set_dn(frml, link_support); 50862306a36Sopenharmony_ci layer_set_up(link_support, frml); 50962306a36Sopenharmony_ci layer_set_dn(link_support, phy_layer); 51062306a36Sopenharmony_ci layer_set_up(phy_layer, link_support); 51162306a36Sopenharmony_ci } else { 51262306a36Sopenharmony_ci layer_set_dn(frml, phy_layer); 51362306a36Sopenharmony_ci layer_set_up(phy_layer, frml); 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci list_add_rcu(&phyinfo->node, &cnfg->phys); 51762306a36Sopenharmony_ciout: 51862306a36Sopenharmony_ci mutex_unlock(&cnfg->lock); 51962306a36Sopenharmony_ci return res; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ciout_err: 52262306a36Sopenharmony_ci kfree(phyinfo); 52362306a36Sopenharmony_ci mutex_unlock(&cnfg->lock); 52462306a36Sopenharmony_ci return res; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ciEXPORT_SYMBOL(cfcnfg_add_phy_layer); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ciint cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, 52962306a36Sopenharmony_ci bool up) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci rcu_read_lock(); 53462306a36Sopenharmony_ci phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); 53562306a36Sopenharmony_ci if (phyinfo == NULL) { 53662306a36Sopenharmony_ci rcu_read_unlock(); 53762306a36Sopenharmony_ci return -ENODEV; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (phyinfo->up == up) { 54162306a36Sopenharmony_ci rcu_read_unlock(); 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci phyinfo->up = up; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (up) { 54762306a36Sopenharmony_ci cffrml_hold(phyinfo->frm_layer); 54862306a36Sopenharmony_ci cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, 54962306a36Sopenharmony_ci phy_layer->id); 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); 55262306a36Sopenharmony_ci cffrml_put(phyinfo->frm_layer); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci rcu_read_unlock(); 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ciEXPORT_SYMBOL(cfcnfg_set_phy_state); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ciint cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct cflayer *frml, *frml_dn; 56362306a36Sopenharmony_ci u16 phyid; 56462306a36Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci might_sleep(); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci mutex_lock(&cnfg->lock); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci phyid = phy_layer->id; 57162306a36Sopenharmony_ci phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (phyinfo == NULL) { 57462306a36Sopenharmony_ci mutex_unlock(&cnfg->lock); 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci caif_assert(phyid == phyinfo->id); 57862306a36Sopenharmony_ci caif_assert(phy_layer == phyinfo->phy_layer); 57962306a36Sopenharmony_ci caif_assert(phy_layer->id == phyid); 58062306a36Sopenharmony_ci caif_assert(phyinfo->frm_layer->id == phyid); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci list_del_rcu(&phyinfo->node); 58362306a36Sopenharmony_ci synchronize_rcu(); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Fail if reference count is not zero */ 58662306a36Sopenharmony_ci if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) { 58762306a36Sopenharmony_ci pr_info("Wait for device inuse\n"); 58862306a36Sopenharmony_ci list_add_rcu(&phyinfo->node, &cnfg->phys); 58962306a36Sopenharmony_ci mutex_unlock(&cnfg->lock); 59062306a36Sopenharmony_ci return -EAGAIN; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci frml = phyinfo->frm_layer; 59462306a36Sopenharmony_ci frml_dn = frml->dn; 59562306a36Sopenharmony_ci cffrml_set_uplayer(frml, NULL); 59662306a36Sopenharmony_ci cffrml_set_dnlayer(frml, NULL); 59762306a36Sopenharmony_ci if (phy_layer != frml_dn) { 59862306a36Sopenharmony_ci layer_set_up(frml_dn, NULL); 59962306a36Sopenharmony_ci layer_set_dn(frml_dn, NULL); 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci layer_set_up(phy_layer, NULL); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (phyinfo->phy_layer != frml_dn) 60462306a36Sopenharmony_ci kfree(frml_dn); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci cffrml_free(frml); 60762306a36Sopenharmony_ci kfree(phyinfo); 60862306a36Sopenharmony_ci mutex_unlock(&cnfg->lock); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ciEXPORT_SYMBOL(cfcnfg_del_phy_layer); 613