xref: /kernel/linux/linux-6.6/net/caif/cfcnfg.c (revision 62306a36)
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, &param);
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, &param, 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