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, &param);
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, &param, 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