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/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/stddef.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h> 158c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h> 168c2ecf20Sopenharmony_ci#include <net/caif/cfcnfg.h> 178c2ecf20Sopenharmony_ci#include <net/caif/cfctrl.h> 188c2ecf20Sopenharmony_ci#include <net/caif/cfmuxl.h> 198c2ecf20Sopenharmony_ci#include <net/caif/cffrml.h> 208c2ecf20Sopenharmony_ci#include <net/caif/cfserl.h> 218c2ecf20Sopenharmony_ci#include <net/caif/cfsrvl.h> 228c2ecf20Sopenharmony_ci#include <net/caif/caif_dev.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define container_obj(layr) container_of(layr, struct cfcnfg, layer) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Information about CAIF physical interfaces held by Config Module in order 278c2ecf20Sopenharmony_ci * to manage physical interfaces 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_cistruct cfcnfg_phyinfo { 308c2ecf20Sopenharmony_ci struct list_head node; 318c2ecf20Sopenharmony_ci bool up; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* Pointer to the layer below the MUX (framing layer) */ 348c2ecf20Sopenharmony_ci struct cflayer *frm_layer; 358c2ecf20Sopenharmony_ci /* Pointer to the lowest actual physical layer */ 368c2ecf20Sopenharmony_ci struct cflayer *phy_layer; 378c2ecf20Sopenharmony_ci /* Unique identifier of the physical interface */ 388c2ecf20Sopenharmony_ci unsigned int id; 398c2ecf20Sopenharmony_ci /* Preference of the physical in interface */ 408c2ecf20Sopenharmony_ci enum cfcnfg_phy_preference pref; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Information about the physical device */ 438c2ecf20Sopenharmony_ci struct dev_info dev_info; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* Interface index */ 468c2ecf20Sopenharmony_ci int ifindex; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Protocol head room added for CAIF link layer */ 498c2ecf20Sopenharmony_ci int head_room; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Use Start of frame checksum */ 528c2ecf20Sopenharmony_ci bool use_fcs; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct cfcnfg { 568c2ecf20Sopenharmony_ci struct cflayer layer; 578c2ecf20Sopenharmony_ci struct cflayer *ctrl; 588c2ecf20Sopenharmony_ci struct cflayer *mux; 598c2ecf20Sopenharmony_ci struct list_head phys; 608c2ecf20Sopenharmony_ci struct mutex lock; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, 648c2ecf20Sopenharmony_ci enum cfctrl_srv serv, u8 phyid, 658c2ecf20Sopenharmony_ci struct cflayer *adapt_layer); 668c2ecf20Sopenharmony_cistatic void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id); 678c2ecf20Sopenharmony_cistatic void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, 688c2ecf20Sopenharmony_ci struct cflayer *adapt_layer); 698c2ecf20Sopenharmony_cistatic void cfctrl_resp_func(void); 708c2ecf20Sopenharmony_cistatic void cfctrl_enum_resp(void); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct cfcnfg *cfcnfg_create(void) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct cfcnfg *this; 758c2ecf20Sopenharmony_ci struct cfctrl_rsp *resp; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci might_sleep(); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Initiate this layer */ 808c2ecf20Sopenharmony_ci this = kzalloc(sizeof(struct cfcnfg), GFP_ATOMIC); 818c2ecf20Sopenharmony_ci if (!this) 828c2ecf20Sopenharmony_ci return NULL; 838c2ecf20Sopenharmony_ci this->mux = cfmuxl_create(); 848c2ecf20Sopenharmony_ci if (!this->mux) 858c2ecf20Sopenharmony_ci goto out_of_mem; 868c2ecf20Sopenharmony_ci this->ctrl = cfctrl_create(); 878c2ecf20Sopenharmony_ci if (!this->ctrl) 888c2ecf20Sopenharmony_ci goto out_of_mem; 898c2ecf20Sopenharmony_ci /* Initiate response functions */ 908c2ecf20Sopenharmony_ci resp = cfctrl_get_respfuncs(this->ctrl); 918c2ecf20Sopenharmony_ci resp->enum_rsp = cfctrl_enum_resp; 928c2ecf20Sopenharmony_ci resp->linkerror_ind = cfctrl_resp_func; 938c2ecf20Sopenharmony_ci resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp; 948c2ecf20Sopenharmony_ci resp->sleep_rsp = cfctrl_resp_func; 958c2ecf20Sopenharmony_ci resp->wake_rsp = cfctrl_resp_func; 968c2ecf20Sopenharmony_ci resp->restart_rsp = cfctrl_resp_func; 978c2ecf20Sopenharmony_ci resp->radioset_rsp = cfctrl_resp_func; 988c2ecf20Sopenharmony_ci resp->linksetup_rsp = cfcnfg_linkup_rsp; 998c2ecf20Sopenharmony_ci resp->reject_rsp = cfcnfg_reject_rsp; 1008c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&this->phys); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci cfmuxl_set_uplayer(this->mux, this->ctrl, 0); 1038c2ecf20Sopenharmony_ci layer_set_dn(this->ctrl, this->mux); 1048c2ecf20Sopenharmony_ci layer_set_up(this->ctrl, this); 1058c2ecf20Sopenharmony_ci mutex_init(&this->lock); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return this; 1088c2ecf20Sopenharmony_ciout_of_mem: 1098c2ecf20Sopenharmony_ci synchronize_rcu(); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci kfree(this->mux); 1128c2ecf20Sopenharmony_ci kfree(this->ctrl); 1138c2ecf20Sopenharmony_ci kfree(this); 1148c2ecf20Sopenharmony_ci return NULL; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_civoid cfcnfg_remove(struct cfcnfg *cfg) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci might_sleep(); 1208c2ecf20Sopenharmony_ci if (cfg) { 1218c2ecf20Sopenharmony_ci synchronize_rcu(); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci kfree(cfg->mux); 1248c2ecf20Sopenharmony_ci cfctrl_remove(cfg->ctrl); 1258c2ecf20Sopenharmony_ci kfree(cfg); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void cfctrl_resp_func(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg, 1348c2ecf20Sopenharmony_ci u8 phyid) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phy; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) 1398c2ecf20Sopenharmony_ci if (phy->id == phyid) 1408c2ecf20Sopenharmony_ci return phy; 1418c2ecf20Sopenharmony_ci return NULL; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void cfctrl_enum_resp(void) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg, 1498c2ecf20Sopenharmony_ci enum cfcnfg_phy_preference phy_pref) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci /* Try to match with specified preference */ 1528c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phy; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) { 1558c2ecf20Sopenharmony_ci if (phy->up && phy->pref == phy_pref && 1568c2ecf20Sopenharmony_ci phy->frm_layer != NULL) 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return &phy->dev_info; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* Otherwise just return something */ 1628c2ecf20Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) 1638c2ecf20Sopenharmony_ci if (phy->up) 1648c2ecf20Sopenharmony_ci return &phy->dev_info; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return NULL; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phy; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci list_for_each_entry_rcu(phy, &cnfg->phys, node) 1748c2ecf20Sopenharmony_ci if (phy->ifindex == ifi && phy->up) 1758c2ecf20Sopenharmony_ci return phy->id; 1768c2ecf20Sopenharmony_ci return -ENODEV; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciint caif_disconnect_client(struct net *net, struct cflayer *adap_layer) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci u8 channel_id; 1828c2ecf20Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(net); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci caif_assert(adap_layer != NULL); 1858c2ecf20Sopenharmony_ci cfctrl_cancel_req(cfg->ctrl, adap_layer); 1868c2ecf20Sopenharmony_ci channel_id = adap_layer->id; 1878c2ecf20Sopenharmony_ci if (channel_id != 0) { 1888c2ecf20Sopenharmony_ci struct cflayer *servl; 1898c2ecf20Sopenharmony_ci servl = cfmuxl_remove_uplayer(cfg->mux, channel_id); 1908c2ecf20Sopenharmony_ci cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer); 1918c2ecf20Sopenharmony_ci if (servl != NULL) 1928c2ecf20Sopenharmony_ci layer_set_up(servl, NULL); 1938c2ecf20Sopenharmony_ci } else 1948c2ecf20Sopenharmony_ci pr_debug("nothing to disconnect\n"); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* Do RCU sync before initiating cleanup */ 1978c2ecf20Sopenharmony_ci synchronize_rcu(); 1988c2ecf20Sopenharmony_ci if (adap_layer->ctrlcmd != NULL) 1998c2ecf20Sopenharmony_ci adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0); 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(caif_disconnect_client); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic const int protohead[CFCTRL_SRV_MASK] = { 2108c2ecf20Sopenharmony_ci [CFCTRL_SRV_VEI] = 4, 2118c2ecf20Sopenharmony_ci [CFCTRL_SRV_DATAGRAM] = 7, 2128c2ecf20Sopenharmony_ci [CFCTRL_SRV_UTIL] = 4, 2138c2ecf20Sopenharmony_ci [CFCTRL_SRV_RFM] = 3, 2148c2ecf20Sopenharmony_ci [CFCTRL_SRV_DBG] = 3, 2158c2ecf20Sopenharmony_ci}; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int caif_connect_req_to_link_param(struct cfcnfg *cnfg, 2198c2ecf20Sopenharmony_ci struct caif_connect_request *s, 2208c2ecf20Sopenharmony_ci struct cfctrl_link_param *l) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci struct dev_info *dev_info; 2238c2ecf20Sopenharmony_ci enum cfcnfg_phy_preference pref; 2248c2ecf20Sopenharmony_ci int res; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci memset(l, 0, sizeof(*l)); 2278c2ecf20Sopenharmony_ci /* In caif protocol low value is high priority */ 2288c2ecf20Sopenharmony_ci l->priority = CAIF_PRIO_MAX - s->priority + 1; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (s->ifindex != 0) { 2318c2ecf20Sopenharmony_ci res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex); 2328c2ecf20Sopenharmony_ci if (res < 0) 2338c2ecf20Sopenharmony_ci return res; 2348c2ecf20Sopenharmony_ci l->phyid = res; 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci switch (s->link_selector) { 2378c2ecf20Sopenharmony_ci case CAIF_LINK_HIGH_BANDW: 2388c2ecf20Sopenharmony_ci pref = CFPHYPREF_HIGH_BW; 2398c2ecf20Sopenharmony_ci break; 2408c2ecf20Sopenharmony_ci case CAIF_LINK_LOW_LATENCY: 2418c2ecf20Sopenharmony_ci pref = CFPHYPREF_LOW_LAT; 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci default: 2448c2ecf20Sopenharmony_ci return -EINVAL; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci dev_info = cfcnfg_get_phyid(cnfg, pref); 2478c2ecf20Sopenharmony_ci if (dev_info == NULL) 2488c2ecf20Sopenharmony_ci return -ENODEV; 2498c2ecf20Sopenharmony_ci l->phyid = dev_info->id; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci switch (s->protocol) { 2528c2ecf20Sopenharmony_ci case CAIFPROTO_AT: 2538c2ecf20Sopenharmony_ci l->linktype = CFCTRL_SRV_VEI; 2548c2ecf20Sopenharmony_ci l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3; 2558c2ecf20Sopenharmony_ci l->chtype = s->sockaddr.u.at.type & 0x3; 2568c2ecf20Sopenharmony_ci break; 2578c2ecf20Sopenharmony_ci case CAIFPROTO_DATAGRAM: 2588c2ecf20Sopenharmony_ci l->linktype = CFCTRL_SRV_DATAGRAM; 2598c2ecf20Sopenharmony_ci l->chtype = 0x00; 2608c2ecf20Sopenharmony_ci l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci case CAIFPROTO_DATAGRAM_LOOP: 2638c2ecf20Sopenharmony_ci l->linktype = CFCTRL_SRV_DATAGRAM; 2648c2ecf20Sopenharmony_ci l->chtype = 0x03; 2658c2ecf20Sopenharmony_ci l->endpoint = 0x00; 2668c2ecf20Sopenharmony_ci l->u.datagram.connid = s->sockaddr.u.dgm.connection_id; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case CAIFPROTO_RFM: 2698c2ecf20Sopenharmony_ci l->linktype = CFCTRL_SRV_RFM; 2708c2ecf20Sopenharmony_ci l->u.datagram.connid = s->sockaddr.u.rfm.connection_id; 2718c2ecf20Sopenharmony_ci strlcpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume, 2728c2ecf20Sopenharmony_ci sizeof(l->u.rfm.volume)); 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci case CAIFPROTO_UTIL: 2758c2ecf20Sopenharmony_ci l->linktype = CFCTRL_SRV_UTIL; 2768c2ecf20Sopenharmony_ci l->endpoint = 0x00; 2778c2ecf20Sopenharmony_ci l->chtype = 0x00; 2788c2ecf20Sopenharmony_ci strlcpy(l->u.utility.name, s->sockaddr.u.util.service, 2798c2ecf20Sopenharmony_ci sizeof(l->u.utility.name)); 2808c2ecf20Sopenharmony_ci caif_assert(sizeof(l->u.utility.name) > 10); 2818c2ecf20Sopenharmony_ci l->u.utility.paramlen = s->param.size; 2828c2ecf20Sopenharmony_ci if (l->u.utility.paramlen > sizeof(l->u.utility.params)) 2838c2ecf20Sopenharmony_ci l->u.utility.paramlen = sizeof(l->u.utility.params); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci memcpy(l->u.utility.params, s->param.data, 2868c2ecf20Sopenharmony_ci l->u.utility.paramlen); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci break; 2898c2ecf20Sopenharmony_ci case CAIFPROTO_DEBUG: 2908c2ecf20Sopenharmony_ci l->linktype = CFCTRL_SRV_DBG; 2918c2ecf20Sopenharmony_ci l->endpoint = s->sockaddr.u.dbg.service; 2928c2ecf20Sopenharmony_ci l->chtype = s->sockaddr.u.dbg.type; 2938c2ecf20Sopenharmony_ci break; 2948c2ecf20Sopenharmony_ci default: 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ciint caif_connect_client(struct net *net, struct caif_connect_request *conn_req, 3018c2ecf20Sopenharmony_ci struct cflayer *adap_layer, int *ifindex, 3028c2ecf20Sopenharmony_ci int *proto_head, int *proto_tail) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct cflayer *frml; 3058c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phy; 3068c2ecf20Sopenharmony_ci int err; 3078c2ecf20Sopenharmony_ci struct cfctrl_link_param param; 3088c2ecf20Sopenharmony_ci struct cfcnfg *cfg = get_cfcnfg(net); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci rcu_read_lock(); 3118c2ecf20Sopenharmony_ci err = caif_connect_req_to_link_param(cfg, conn_req, ¶m); 3128c2ecf20Sopenharmony_ci if (err) 3138c2ecf20Sopenharmony_ci goto unlock; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid); 3168c2ecf20Sopenharmony_ci if (!phy) { 3178c2ecf20Sopenharmony_ci err = -ENODEV; 3188c2ecf20Sopenharmony_ci goto unlock; 3198c2ecf20Sopenharmony_ci } 3208c2ecf20Sopenharmony_ci err = -EINVAL; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (adap_layer == NULL) { 3238c2ecf20Sopenharmony_ci pr_err("adap_layer is zero\n"); 3248c2ecf20Sopenharmony_ci goto unlock; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci if (adap_layer->receive == NULL) { 3278c2ecf20Sopenharmony_ci pr_err("adap_layer->receive is NULL\n"); 3288c2ecf20Sopenharmony_ci goto unlock; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci if (adap_layer->ctrlcmd == NULL) { 3318c2ecf20Sopenharmony_ci pr_err("adap_layer->ctrlcmd == NULL\n"); 3328c2ecf20Sopenharmony_ci goto unlock; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci err = -ENODEV; 3368c2ecf20Sopenharmony_ci frml = phy->frm_layer; 3378c2ecf20Sopenharmony_ci if (frml == NULL) { 3388c2ecf20Sopenharmony_ci pr_err("Specified PHY type does not exist!\n"); 3398c2ecf20Sopenharmony_ci goto unlock; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci caif_assert(param.phyid == phy->id); 3428c2ecf20Sopenharmony_ci caif_assert(phy->frm_layer->id == 3438c2ecf20Sopenharmony_ci param.phyid); 3448c2ecf20Sopenharmony_ci caif_assert(phy->phy_layer->id == 3458c2ecf20Sopenharmony_ci param.phyid); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci *ifindex = phy->ifindex; 3488c2ecf20Sopenharmony_ci *proto_tail = 2; 3498c2ecf20Sopenharmony_ci *proto_head = protohead[param.linktype] + phy->head_room; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci rcu_read_unlock(); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */ 3548c2ecf20Sopenharmony_ci cfctrl_enum_req(cfg->ctrl, param.phyid); 3558c2ecf20Sopenharmony_ci return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ciunlock: 3588c2ecf20Sopenharmony_ci rcu_read_unlock(); 3598c2ecf20Sopenharmony_ci return err; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(caif_connect_client); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id, 3648c2ecf20Sopenharmony_ci struct cflayer *adapt_layer) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) 3678c2ecf20Sopenharmony_ci adapt_layer->ctrlcmd(adapt_layer, 3688c2ecf20Sopenharmony_ci CAIF_CTRLCMD_INIT_FAIL_RSP, 0); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic void 3728c2ecf20Sopenharmony_cicfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv, 3738c2ecf20Sopenharmony_ci u8 phyid, struct cflayer *adapt_layer) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct cfcnfg *cnfg = container_obj(layer); 3768c2ecf20Sopenharmony_ci struct cflayer *servicel = NULL; 3778c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo; 3788c2ecf20Sopenharmony_ci struct net_device *netdev; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (channel_id == 0) { 3818c2ecf20Sopenharmony_ci pr_warn("received channel_id zero\n"); 3828c2ecf20Sopenharmony_ci if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL) 3838c2ecf20Sopenharmony_ci adapt_layer->ctrlcmd(adapt_layer, 3848c2ecf20Sopenharmony_ci CAIF_CTRLCMD_INIT_FAIL_RSP, 0); 3858c2ecf20Sopenharmony_ci return; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci rcu_read_lock(); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (adapt_layer == NULL) { 3918c2ecf20Sopenharmony_ci pr_debug("link setup response but no client exist, send linkdown back\n"); 3928c2ecf20Sopenharmony_ci cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL); 3938c2ecf20Sopenharmony_ci goto unlock; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci caif_assert(cnfg != NULL); 3978c2ecf20Sopenharmony_ci caif_assert(phyid != 0); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); 4008c2ecf20Sopenharmony_ci if (phyinfo == NULL) { 4018c2ecf20Sopenharmony_ci pr_err("ERROR: Link Layer Device disappeared while connecting\n"); 4028c2ecf20Sopenharmony_ci goto unlock; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci caif_assert(phyinfo != NULL); 4068c2ecf20Sopenharmony_ci caif_assert(phyinfo->id == phyid); 4078c2ecf20Sopenharmony_ci caif_assert(phyinfo->phy_layer != NULL); 4088c2ecf20Sopenharmony_ci caif_assert(phyinfo->phy_layer->id == phyid); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci adapt_layer->id = channel_id; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci switch (serv) { 4138c2ecf20Sopenharmony_ci case CFCTRL_SRV_VEI: 4148c2ecf20Sopenharmony_ci servicel = cfvei_create(channel_id, &phyinfo->dev_info); 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci case CFCTRL_SRV_DATAGRAM: 4178c2ecf20Sopenharmony_ci servicel = cfdgml_create(channel_id, 4188c2ecf20Sopenharmony_ci &phyinfo->dev_info); 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci case CFCTRL_SRV_RFM: 4218c2ecf20Sopenharmony_ci netdev = phyinfo->dev_info.dev; 4228c2ecf20Sopenharmony_ci servicel = cfrfml_create(channel_id, &phyinfo->dev_info, 4238c2ecf20Sopenharmony_ci netdev->mtu); 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci case CFCTRL_SRV_UTIL: 4268c2ecf20Sopenharmony_ci servicel = cfutill_create(channel_id, &phyinfo->dev_info); 4278c2ecf20Sopenharmony_ci break; 4288c2ecf20Sopenharmony_ci case CFCTRL_SRV_VIDEO: 4298c2ecf20Sopenharmony_ci servicel = cfvidl_create(channel_id, &phyinfo->dev_info); 4308c2ecf20Sopenharmony_ci break; 4318c2ecf20Sopenharmony_ci case CFCTRL_SRV_DBG: 4328c2ecf20Sopenharmony_ci servicel = cfdbgl_create(channel_id, &phyinfo->dev_info); 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci default: 4358c2ecf20Sopenharmony_ci pr_err("Protocol error. Link setup response - unknown channel type\n"); 4368c2ecf20Sopenharmony_ci goto unlock; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci if (!servicel) 4398c2ecf20Sopenharmony_ci goto unlock; 4408c2ecf20Sopenharmony_ci layer_set_dn(servicel, cnfg->mux); 4418c2ecf20Sopenharmony_ci cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id); 4428c2ecf20Sopenharmony_ci layer_set_up(servicel, adapt_layer); 4438c2ecf20Sopenharmony_ci layer_set_dn(adapt_layer, servicel); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci rcu_read_unlock(); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0); 4488c2ecf20Sopenharmony_ci return; 4498c2ecf20Sopenharmony_ciunlock: 4508c2ecf20Sopenharmony_ci rcu_read_unlock(); 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ciint 4548c2ecf20Sopenharmony_cicfcnfg_add_phy_layer(struct cfcnfg *cnfg, 4558c2ecf20Sopenharmony_ci struct net_device *dev, struct cflayer *phy_layer, 4568c2ecf20Sopenharmony_ci enum cfcnfg_phy_preference pref, 4578c2ecf20Sopenharmony_ci struct cflayer *link_support, 4588c2ecf20Sopenharmony_ci bool fcs, int head_room) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct cflayer *frml; 4618c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo = NULL; 4628c2ecf20Sopenharmony_ci int i, res = 0; 4638c2ecf20Sopenharmony_ci u8 phyid; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci mutex_lock(&cnfg->lock); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* CAIF protocol allow maximum 6 link-layers */ 4688c2ecf20Sopenharmony_ci for (i = 0; i < 7; i++) { 4698c2ecf20Sopenharmony_ci phyid = (dev->ifindex + i) & 0x7; 4708c2ecf20Sopenharmony_ci if (phyid == 0) 4718c2ecf20Sopenharmony_ci continue; 4728c2ecf20Sopenharmony_ci if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL) 4738c2ecf20Sopenharmony_ci goto got_phyid; 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci pr_warn("Too many CAIF Link Layers (max 6)\n"); 4768c2ecf20Sopenharmony_ci res = -EEXIST; 4778c2ecf20Sopenharmony_ci goto out; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cigot_phyid: 4808c2ecf20Sopenharmony_ci phyinfo = kzalloc(sizeof(struct cfcnfg_phyinfo), GFP_ATOMIC); 4818c2ecf20Sopenharmony_ci if (!phyinfo) { 4828c2ecf20Sopenharmony_ci res = -ENOMEM; 4838c2ecf20Sopenharmony_ci goto out_err; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci phy_layer->id = phyid; 4878c2ecf20Sopenharmony_ci phyinfo->pref = pref; 4888c2ecf20Sopenharmony_ci phyinfo->id = phyid; 4898c2ecf20Sopenharmony_ci phyinfo->dev_info.id = phyid; 4908c2ecf20Sopenharmony_ci phyinfo->dev_info.dev = dev; 4918c2ecf20Sopenharmony_ci phyinfo->phy_layer = phy_layer; 4928c2ecf20Sopenharmony_ci phyinfo->ifindex = dev->ifindex; 4938c2ecf20Sopenharmony_ci phyinfo->head_room = head_room; 4948c2ecf20Sopenharmony_ci phyinfo->use_fcs = fcs; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci frml = cffrml_create(phyid, fcs); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (!frml) { 4998c2ecf20Sopenharmony_ci res = -ENOMEM; 5008c2ecf20Sopenharmony_ci goto out_err; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci phyinfo->frm_layer = frml; 5038c2ecf20Sopenharmony_ci layer_set_up(frml, cnfg->mux); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (link_support != NULL) { 5068c2ecf20Sopenharmony_ci link_support->id = phyid; 5078c2ecf20Sopenharmony_ci layer_set_dn(frml, link_support); 5088c2ecf20Sopenharmony_ci layer_set_up(link_support, frml); 5098c2ecf20Sopenharmony_ci layer_set_dn(link_support, phy_layer); 5108c2ecf20Sopenharmony_ci layer_set_up(phy_layer, link_support); 5118c2ecf20Sopenharmony_ci } else { 5128c2ecf20Sopenharmony_ci layer_set_dn(frml, phy_layer); 5138c2ecf20Sopenharmony_ci layer_set_up(phy_layer, frml); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci list_add_rcu(&phyinfo->node, &cnfg->phys); 5178c2ecf20Sopenharmony_ciout: 5188c2ecf20Sopenharmony_ci mutex_unlock(&cnfg->lock); 5198c2ecf20Sopenharmony_ci return res; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ciout_err: 5228c2ecf20Sopenharmony_ci kfree(phyinfo); 5238c2ecf20Sopenharmony_ci mutex_unlock(&cnfg->lock); 5248c2ecf20Sopenharmony_ci return res; 5258c2ecf20Sopenharmony_ci} 5268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfcnfg_add_phy_layer); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ciint cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer, 5298c2ecf20Sopenharmony_ci bool up) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci rcu_read_lock(); 5348c2ecf20Sopenharmony_ci phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id); 5358c2ecf20Sopenharmony_ci if (phyinfo == NULL) { 5368c2ecf20Sopenharmony_ci rcu_read_unlock(); 5378c2ecf20Sopenharmony_ci return -ENODEV; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (phyinfo->up == up) { 5418c2ecf20Sopenharmony_ci rcu_read_unlock(); 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci phyinfo->up = up; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (up) { 5478c2ecf20Sopenharmony_ci cffrml_hold(phyinfo->frm_layer); 5488c2ecf20Sopenharmony_ci cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer, 5498c2ecf20Sopenharmony_ci phy_layer->id); 5508c2ecf20Sopenharmony_ci } else { 5518c2ecf20Sopenharmony_ci cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id); 5528c2ecf20Sopenharmony_ci cffrml_put(phyinfo->frm_layer); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci rcu_read_unlock(); 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfcnfg_set_phy_state); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ciint cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct cflayer *frml, *frml_dn; 5638c2ecf20Sopenharmony_ci u16 phyid; 5648c2ecf20Sopenharmony_ci struct cfcnfg_phyinfo *phyinfo; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci might_sleep(); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci mutex_lock(&cnfg->lock); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci phyid = phy_layer->id; 5718c2ecf20Sopenharmony_ci phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (phyinfo == NULL) { 5748c2ecf20Sopenharmony_ci mutex_unlock(&cnfg->lock); 5758c2ecf20Sopenharmony_ci return 0; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci caif_assert(phyid == phyinfo->id); 5788c2ecf20Sopenharmony_ci caif_assert(phy_layer == phyinfo->phy_layer); 5798c2ecf20Sopenharmony_ci caif_assert(phy_layer->id == phyid); 5808c2ecf20Sopenharmony_ci caif_assert(phyinfo->frm_layer->id == phyid); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci list_del_rcu(&phyinfo->node); 5838c2ecf20Sopenharmony_ci synchronize_rcu(); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Fail if reference count is not zero */ 5868c2ecf20Sopenharmony_ci if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) { 5878c2ecf20Sopenharmony_ci pr_info("Wait for device inuse\n"); 5888c2ecf20Sopenharmony_ci list_add_rcu(&phyinfo->node, &cnfg->phys); 5898c2ecf20Sopenharmony_ci mutex_unlock(&cnfg->lock); 5908c2ecf20Sopenharmony_ci return -EAGAIN; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci frml = phyinfo->frm_layer; 5948c2ecf20Sopenharmony_ci frml_dn = frml->dn; 5958c2ecf20Sopenharmony_ci cffrml_set_uplayer(frml, NULL); 5968c2ecf20Sopenharmony_ci cffrml_set_dnlayer(frml, NULL); 5978c2ecf20Sopenharmony_ci if (phy_layer != frml_dn) { 5988c2ecf20Sopenharmony_ci layer_set_up(frml_dn, NULL); 5998c2ecf20Sopenharmony_ci layer_set_dn(frml_dn, NULL); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci layer_set_up(phy_layer, NULL); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci if (phyinfo->phy_layer != frml_dn) 6048c2ecf20Sopenharmony_ci kfree(frml_dn); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci cffrml_free(frml); 6078c2ecf20Sopenharmony_ci kfree(phyinfo); 6088c2ecf20Sopenharmony_ci mutex_unlock(&cnfg->lock); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 0; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cfcnfg_del_phy_layer); 613