xref: /kernel/linux/linux-6.6/net/caif/cfctrl.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/stddef.h>
1062306a36Sopenharmony_ci#include <linux/spinlock.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/pkt_sched.h>
1362306a36Sopenharmony_ci#include <net/caif/caif_layer.h>
1462306a36Sopenharmony_ci#include <net/caif/cfpkt.h>
1562306a36Sopenharmony_ci#include <net/caif/cfctrl.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define container_obj(layr) container_of(layr, struct cfctrl, serv.layer)
1862306a36Sopenharmony_ci#define UTILITY_NAME_LENGTH 16
1962306a36Sopenharmony_ci#define CFPKT_CTRL_PKT_LEN 20
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#ifdef CAIF_NO_LOOP
2262306a36Sopenharmony_cistatic int handle_loop(struct cfctrl *ctrl,
2362306a36Sopenharmony_ci		       int cmd, struct cfpkt *pkt){
2462306a36Sopenharmony_ci	return -1;
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci#else
2762306a36Sopenharmony_cistatic int handle_loop(struct cfctrl *ctrl,
2862306a36Sopenharmony_ci		       int cmd, struct cfpkt *pkt);
2962306a36Sopenharmony_ci#endif
3062306a36Sopenharmony_cistatic int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt);
3162306a36Sopenharmony_cistatic void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
3262306a36Sopenharmony_ci			   int phyid);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct cflayer *cfctrl_create(void)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct dev_info dev_info;
3862306a36Sopenharmony_ci	struct cfctrl *this =
3962306a36Sopenharmony_ci		kzalloc(sizeof(struct cfctrl), GFP_ATOMIC);
4062306a36Sopenharmony_ci	if (!this)
4162306a36Sopenharmony_ci		return NULL;
4262306a36Sopenharmony_ci	caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
4362306a36Sopenharmony_ci	memset(&dev_info, 0, sizeof(dev_info));
4462306a36Sopenharmony_ci	dev_info.id = 0xff;
4562306a36Sopenharmony_ci	cfsrvl_init(&this->serv, 0, &dev_info, false);
4662306a36Sopenharmony_ci	atomic_set(&this->req_seq_no, 1);
4762306a36Sopenharmony_ci	atomic_set(&this->rsp_seq_no, 1);
4862306a36Sopenharmony_ci	this->serv.layer.receive = cfctrl_recv;
4962306a36Sopenharmony_ci	sprintf(this->serv.layer.name, "ctrl");
5062306a36Sopenharmony_ci	this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
5162306a36Sopenharmony_ci#ifndef CAIF_NO_LOOP
5262306a36Sopenharmony_ci	spin_lock_init(&this->loop_linkid_lock);
5362306a36Sopenharmony_ci	this->loop_linkid = 1;
5462306a36Sopenharmony_ci#endif
5562306a36Sopenharmony_ci	spin_lock_init(&this->info_list_lock);
5662306a36Sopenharmony_ci	INIT_LIST_HEAD(&this->list);
5762306a36Sopenharmony_ci	return &this->serv.layer;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_civoid cfctrl_remove(struct cflayer *layer)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct cfctrl_request_info *p, *tmp;
6362306a36Sopenharmony_ci	struct cfctrl *ctrl = container_obj(layer);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	spin_lock_bh(&ctrl->info_list_lock);
6662306a36Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
6762306a36Sopenharmony_ci		list_del(&p->list);
6862306a36Sopenharmony_ci		kfree(p);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci	spin_unlock_bh(&ctrl->info_list_lock);
7162306a36Sopenharmony_ci	kfree(layer);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic bool param_eq(const struct cfctrl_link_param *p1,
7562306a36Sopenharmony_ci		     const struct cfctrl_link_param *p2)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	bool eq =
7862306a36Sopenharmony_ci	    p1->linktype == p2->linktype &&
7962306a36Sopenharmony_ci	    p1->priority == p2->priority &&
8062306a36Sopenharmony_ci	    p1->phyid == p2->phyid &&
8162306a36Sopenharmony_ci	    p1->endpoint == p2->endpoint && p1->chtype == p2->chtype;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!eq)
8462306a36Sopenharmony_ci		return false;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	switch (p1->linktype) {
8762306a36Sopenharmony_ci	case CFCTRL_SRV_VEI:
8862306a36Sopenharmony_ci		return true;
8962306a36Sopenharmony_ci	case CFCTRL_SRV_DATAGRAM:
9062306a36Sopenharmony_ci		return p1->u.datagram.connid == p2->u.datagram.connid;
9162306a36Sopenharmony_ci	case CFCTRL_SRV_RFM:
9262306a36Sopenharmony_ci		return
9362306a36Sopenharmony_ci		    p1->u.rfm.connid == p2->u.rfm.connid &&
9462306a36Sopenharmony_ci		    strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0;
9562306a36Sopenharmony_ci	case CFCTRL_SRV_UTIL:
9662306a36Sopenharmony_ci		return
9762306a36Sopenharmony_ci		    p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb
9862306a36Sopenharmony_ci		    && p1->u.utility.fifosize_bufs ==
9962306a36Sopenharmony_ci		    p2->u.utility.fifosize_bufs
10062306a36Sopenharmony_ci		    && strcmp(p1->u.utility.name, p2->u.utility.name) == 0
10162306a36Sopenharmony_ci		    && p1->u.utility.paramlen == p2->u.utility.paramlen
10262306a36Sopenharmony_ci		    && memcmp(p1->u.utility.params, p2->u.utility.params,
10362306a36Sopenharmony_ci			      p1->u.utility.paramlen) == 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	case CFCTRL_SRV_VIDEO:
10662306a36Sopenharmony_ci		return p1->u.video.connid == p2->u.video.connid;
10762306a36Sopenharmony_ci	case CFCTRL_SRV_DBG:
10862306a36Sopenharmony_ci		return true;
10962306a36Sopenharmony_ci	case CFCTRL_SRV_DECM:
11062306a36Sopenharmony_ci		return false;
11162306a36Sopenharmony_ci	default:
11262306a36Sopenharmony_ci		return false;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci	return false;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic bool cfctrl_req_eq(const struct cfctrl_request_info *r1,
11862306a36Sopenharmony_ci			  const struct cfctrl_request_info *r2)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	if (r1->cmd != r2->cmd)
12162306a36Sopenharmony_ci		return false;
12262306a36Sopenharmony_ci	if (r1->cmd == CFCTRL_CMD_LINK_SETUP)
12362306a36Sopenharmony_ci		return param_eq(&r1->param, &r2->param);
12462306a36Sopenharmony_ci	else
12562306a36Sopenharmony_ci		return r1->channel_id == r2->channel_id;
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci/* Insert request at the end */
12962306a36Sopenharmony_cistatic void cfctrl_insert_req(struct cfctrl *ctrl,
13062306a36Sopenharmony_ci			      struct cfctrl_request_info *req)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	spin_lock_bh(&ctrl->info_list_lock);
13362306a36Sopenharmony_ci	atomic_inc(&ctrl->req_seq_no);
13462306a36Sopenharmony_ci	req->sequence_no = atomic_read(&ctrl->req_seq_no);
13562306a36Sopenharmony_ci	list_add_tail(&req->list, &ctrl->list);
13662306a36Sopenharmony_ci	spin_unlock_bh(&ctrl->info_list_lock);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/* Compare and remove request */
14062306a36Sopenharmony_cistatic struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
14162306a36Sopenharmony_ci						struct cfctrl_request_info *req)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	struct cfctrl_request_info *p, *tmp, *first;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
14862306a36Sopenharmony_ci		if (cfctrl_req_eq(req, p)) {
14962306a36Sopenharmony_ci			if (p != first)
15062306a36Sopenharmony_ci				pr_warn("Requests are not received in order\n");
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci			atomic_set(&ctrl->rsp_seq_no,
15362306a36Sopenharmony_ci					 p->sequence_no);
15462306a36Sopenharmony_ci			list_del(&p->list);
15562306a36Sopenharmony_ci			goto out;
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci	p = NULL;
15962306a36Sopenharmony_ciout:
16062306a36Sopenharmony_ci	return p;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistruct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct cfctrl *this = container_obj(layer);
16662306a36Sopenharmony_ci	return &this->res;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	info->hdr_len = 0;
17262306a36Sopenharmony_ci	info->channel_id = cfctrl->serv.layer.id;
17362306a36Sopenharmony_ci	info->dev_info = &cfctrl->serv.dev_info;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_civoid cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	struct cfpkt *pkt;
17962306a36Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
18062306a36Sopenharmony_ci	struct cflayer *dn = cfctrl->serv.layer.dn;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!dn) {
18362306a36Sopenharmony_ci		pr_debug("not able to send enum request\n");
18462306a36Sopenharmony_ci		return;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
18762306a36Sopenharmony_ci	if (!pkt)
18862306a36Sopenharmony_ci		return;
18962306a36Sopenharmony_ci	caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
19062306a36Sopenharmony_ci	init_info(cfpkt_info(pkt), cfctrl);
19162306a36Sopenharmony_ci	cfpkt_info(pkt)->dev_info->id = physlinkid;
19262306a36Sopenharmony_ci	cfctrl->serv.dev_info.id = physlinkid;
19362306a36Sopenharmony_ci	cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
19462306a36Sopenharmony_ci	cfpkt_addbdy(pkt, physlinkid);
19562306a36Sopenharmony_ci	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
19662306a36Sopenharmony_ci	dn->transmit(dn, pkt);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ciint cfctrl_linkup_request(struct cflayer *layer,
20062306a36Sopenharmony_ci			  struct cfctrl_link_param *param,
20162306a36Sopenharmony_ci			  struct cflayer *user_layer)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
20462306a36Sopenharmony_ci	u32 tmp32;
20562306a36Sopenharmony_ci	u16 tmp16;
20662306a36Sopenharmony_ci	u8 tmp8;
20762306a36Sopenharmony_ci	struct cfctrl_request_info *req;
20862306a36Sopenharmony_ci	int ret;
20962306a36Sopenharmony_ci	char utility_name[16];
21062306a36Sopenharmony_ci	struct cfpkt *pkt;
21162306a36Sopenharmony_ci	struct cflayer *dn = cfctrl->serv.layer.dn;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	if (!dn) {
21462306a36Sopenharmony_ci		pr_debug("not able to send linkup request\n");
21562306a36Sopenharmony_ci		return -ENODEV;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (cfctrl_cancel_req(layer, user_layer) > 0) {
21962306a36Sopenharmony_ci		/* Slight Paranoia, check if already connecting */
22062306a36Sopenharmony_ci		pr_err("Duplicate connect request for same client\n");
22162306a36Sopenharmony_ci		WARN_ON(1);
22262306a36Sopenharmony_ci		return -EALREADY;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
22662306a36Sopenharmony_ci	if (!pkt)
22762306a36Sopenharmony_ci		return -ENOMEM;
22862306a36Sopenharmony_ci	cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
22962306a36Sopenharmony_ci	cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype);
23062306a36Sopenharmony_ci	cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid);
23162306a36Sopenharmony_ci	cfpkt_addbdy(pkt, param->endpoint & 0x03);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	switch (param->linktype) {
23462306a36Sopenharmony_ci	case CFCTRL_SRV_VEI:
23562306a36Sopenharmony_ci		break;
23662306a36Sopenharmony_ci	case CFCTRL_SRV_VIDEO:
23762306a36Sopenharmony_ci		cfpkt_addbdy(pkt, (u8) param->u.video.connid);
23862306a36Sopenharmony_ci		break;
23962306a36Sopenharmony_ci	case CFCTRL_SRV_DBG:
24062306a36Sopenharmony_ci		break;
24162306a36Sopenharmony_ci	case CFCTRL_SRV_DATAGRAM:
24262306a36Sopenharmony_ci		tmp32 = cpu_to_le32(param->u.datagram.connid);
24362306a36Sopenharmony_ci		cfpkt_add_body(pkt, &tmp32, 4);
24462306a36Sopenharmony_ci		break;
24562306a36Sopenharmony_ci	case CFCTRL_SRV_RFM:
24662306a36Sopenharmony_ci		/* Construct a frame, convert DatagramConnectionID to network
24762306a36Sopenharmony_ci		 * format long and copy it out...
24862306a36Sopenharmony_ci		 */
24962306a36Sopenharmony_ci		tmp32 = cpu_to_le32(param->u.rfm.connid);
25062306a36Sopenharmony_ci		cfpkt_add_body(pkt, &tmp32, 4);
25162306a36Sopenharmony_ci		/* Add volume name, including zero termination... */
25262306a36Sopenharmony_ci		cfpkt_add_body(pkt, param->u.rfm.volume,
25362306a36Sopenharmony_ci			       strlen(param->u.rfm.volume) + 1);
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	case CFCTRL_SRV_UTIL:
25662306a36Sopenharmony_ci		tmp16 = cpu_to_le16(param->u.utility.fifosize_kb);
25762306a36Sopenharmony_ci		cfpkt_add_body(pkt, &tmp16, 2);
25862306a36Sopenharmony_ci		tmp16 = cpu_to_le16(param->u.utility.fifosize_bufs);
25962306a36Sopenharmony_ci		cfpkt_add_body(pkt, &tmp16, 2);
26062306a36Sopenharmony_ci		memset(utility_name, 0, sizeof(utility_name));
26162306a36Sopenharmony_ci		strscpy(utility_name, param->u.utility.name,
26262306a36Sopenharmony_ci			UTILITY_NAME_LENGTH);
26362306a36Sopenharmony_ci		cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH);
26462306a36Sopenharmony_ci		tmp8 = param->u.utility.paramlen;
26562306a36Sopenharmony_ci		cfpkt_add_body(pkt, &tmp8, 1);
26662306a36Sopenharmony_ci		cfpkt_add_body(pkt, param->u.utility.params,
26762306a36Sopenharmony_ci			       param->u.utility.paramlen);
26862306a36Sopenharmony_ci		break;
26962306a36Sopenharmony_ci	default:
27062306a36Sopenharmony_ci		pr_warn("Request setup of bad link type = %d\n",
27162306a36Sopenharmony_ci			param->linktype);
27262306a36Sopenharmony_ci		cfpkt_destroy(pkt);
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
27662306a36Sopenharmony_ci	if (!req) {
27762306a36Sopenharmony_ci		cfpkt_destroy(pkt);
27862306a36Sopenharmony_ci		return -ENOMEM;
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	req->client_layer = user_layer;
28262306a36Sopenharmony_ci	req->cmd = CFCTRL_CMD_LINK_SETUP;
28362306a36Sopenharmony_ci	req->param = *param;
28462306a36Sopenharmony_ci	cfctrl_insert_req(cfctrl, req);
28562306a36Sopenharmony_ci	init_info(cfpkt_info(pkt), cfctrl);
28662306a36Sopenharmony_ci	/*
28762306a36Sopenharmony_ci	 * NOTE:Always send linkup and linkdown request on the same
28862306a36Sopenharmony_ci	 *	device as the payload. Otherwise old queued up payload
28962306a36Sopenharmony_ci	 *	might arrive with the newly allocated channel ID.
29062306a36Sopenharmony_ci	 */
29162306a36Sopenharmony_ci	cfpkt_info(pkt)->dev_info->id = param->phyid;
29262306a36Sopenharmony_ci	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
29362306a36Sopenharmony_ci	ret =
29462306a36Sopenharmony_ci	    dn->transmit(dn, pkt);
29562306a36Sopenharmony_ci	if (ret < 0) {
29662306a36Sopenharmony_ci		int count;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci		count = cfctrl_cancel_req(&cfctrl->serv.layer,
29962306a36Sopenharmony_ci						user_layer);
30062306a36Sopenharmony_ci		if (count != 1) {
30162306a36Sopenharmony_ci			pr_err("Could not remove request (%d)", count);
30262306a36Sopenharmony_ci			return -ENODEV;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci	return 0;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciint cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
30962306a36Sopenharmony_ci			struct cflayer *client)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	int ret;
31262306a36Sopenharmony_ci	struct cfpkt *pkt;
31362306a36Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
31462306a36Sopenharmony_ci	struct cflayer *dn = cfctrl->serv.layer.dn;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (!dn) {
31762306a36Sopenharmony_ci		pr_debug("not able to send link-down request\n");
31862306a36Sopenharmony_ci		return -ENODEV;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
32162306a36Sopenharmony_ci	if (!pkt)
32262306a36Sopenharmony_ci		return -ENOMEM;
32362306a36Sopenharmony_ci	cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
32462306a36Sopenharmony_ci	cfpkt_addbdy(pkt, channelid);
32562306a36Sopenharmony_ci	init_info(cfpkt_info(pkt), cfctrl);
32662306a36Sopenharmony_ci	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
32762306a36Sopenharmony_ci	ret =
32862306a36Sopenharmony_ci	    dn->transmit(dn, pkt);
32962306a36Sopenharmony_ci#ifndef CAIF_NO_LOOP
33062306a36Sopenharmony_ci	cfctrl->loop_linkused[channelid] = 0;
33162306a36Sopenharmony_ci#endif
33262306a36Sopenharmony_ci	return ret;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ciint cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	struct cfctrl_request_info *p, *tmp;
33862306a36Sopenharmony_ci	struct cfctrl *ctrl = container_obj(layr);
33962306a36Sopenharmony_ci	int found = 0;
34062306a36Sopenharmony_ci	spin_lock_bh(&ctrl->info_list_lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
34362306a36Sopenharmony_ci		if (p->client_layer == adap_layer) {
34462306a36Sopenharmony_ci			list_del(&p->list);
34562306a36Sopenharmony_ci			kfree(p);
34662306a36Sopenharmony_ci			found++;
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	spin_unlock_bh(&ctrl->info_list_lock);
35162306a36Sopenharmony_ci	return found;
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	u8 cmdrsp;
35762306a36Sopenharmony_ci	u8 cmd;
35862306a36Sopenharmony_ci	int ret = -1;
35962306a36Sopenharmony_ci	u8 len;
36062306a36Sopenharmony_ci	u8 param[255];
36162306a36Sopenharmony_ci	u8 linkid = 0;
36262306a36Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
36362306a36Sopenharmony_ci	struct cfctrl_request_info rsp, *req;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	cmdrsp = cfpkt_extr_head_u8(pkt);
36762306a36Sopenharmony_ci	cmd = cmdrsp & CFCTRL_CMD_MASK;
36862306a36Sopenharmony_ci	if (cmd != CFCTRL_CMD_LINK_ERR
36962306a36Sopenharmony_ci	    && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)
37062306a36Sopenharmony_ci		&& CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) {
37162306a36Sopenharmony_ci		if (handle_loop(cfctrl, cmd, pkt) != 0)
37262306a36Sopenharmony_ci			cmdrsp |= CFCTRL_ERR_BIT;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	switch (cmd) {
37662306a36Sopenharmony_ci	case CFCTRL_CMD_LINK_SETUP:
37762306a36Sopenharmony_ci		{
37862306a36Sopenharmony_ci			enum cfctrl_srv serv;
37962306a36Sopenharmony_ci			enum cfctrl_srv servtype;
38062306a36Sopenharmony_ci			u8 endpoint;
38162306a36Sopenharmony_ci			u8 physlinkid;
38262306a36Sopenharmony_ci			u8 prio;
38362306a36Sopenharmony_ci			u8 tmp;
38462306a36Sopenharmony_ci			u8 *cp;
38562306a36Sopenharmony_ci			int i;
38662306a36Sopenharmony_ci			struct cfctrl_link_param linkparam;
38762306a36Sopenharmony_ci			memset(&linkparam, 0, sizeof(linkparam));
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci			tmp = cfpkt_extr_head_u8(pkt);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci			serv = tmp & CFCTRL_SRV_MASK;
39262306a36Sopenharmony_ci			linkparam.linktype = serv;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci			servtype = tmp >> 4;
39562306a36Sopenharmony_ci			linkparam.chtype = servtype;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci			tmp = cfpkt_extr_head_u8(pkt);
39862306a36Sopenharmony_ci			physlinkid = tmp & 0x07;
39962306a36Sopenharmony_ci			prio = tmp >> 3;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci			linkparam.priority = prio;
40262306a36Sopenharmony_ci			linkparam.phyid = physlinkid;
40362306a36Sopenharmony_ci			endpoint = cfpkt_extr_head_u8(pkt);
40462306a36Sopenharmony_ci			linkparam.endpoint = endpoint & 0x03;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci			switch (serv) {
40762306a36Sopenharmony_ci			case CFCTRL_SRV_VEI:
40862306a36Sopenharmony_ci			case CFCTRL_SRV_DBG:
40962306a36Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
41062306a36Sopenharmony_ci					break;
41162306a36Sopenharmony_ci				/* Link ID */
41262306a36Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
41362306a36Sopenharmony_ci				break;
41462306a36Sopenharmony_ci			case CFCTRL_SRV_VIDEO:
41562306a36Sopenharmony_ci				tmp = cfpkt_extr_head_u8(pkt);
41662306a36Sopenharmony_ci				linkparam.u.video.connid = tmp;
41762306a36Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
41862306a36Sopenharmony_ci					break;
41962306a36Sopenharmony_ci				/* Link ID */
42062306a36Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
42162306a36Sopenharmony_ci				break;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci			case CFCTRL_SRV_DATAGRAM:
42462306a36Sopenharmony_ci				linkparam.u.datagram.connid =
42562306a36Sopenharmony_ci				    cfpkt_extr_head_u32(pkt);
42662306a36Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
42762306a36Sopenharmony_ci					break;
42862306a36Sopenharmony_ci				/* Link ID */
42962306a36Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
43062306a36Sopenharmony_ci				break;
43162306a36Sopenharmony_ci			case CFCTRL_SRV_RFM:
43262306a36Sopenharmony_ci				/* Construct a frame, convert
43362306a36Sopenharmony_ci				 * DatagramConnectionID
43462306a36Sopenharmony_ci				 * to network format long and copy it out...
43562306a36Sopenharmony_ci				 */
43662306a36Sopenharmony_ci				linkparam.u.rfm.connid =
43762306a36Sopenharmony_ci				    cfpkt_extr_head_u32(pkt);
43862306a36Sopenharmony_ci				cp = (u8 *) linkparam.u.rfm.volume;
43962306a36Sopenharmony_ci				for (tmp = cfpkt_extr_head_u8(pkt);
44062306a36Sopenharmony_ci				     cfpkt_more(pkt) && tmp != '\0';
44162306a36Sopenharmony_ci				     tmp = cfpkt_extr_head_u8(pkt))
44262306a36Sopenharmony_ci					*cp++ = tmp;
44362306a36Sopenharmony_ci				*cp = '\0';
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
44662306a36Sopenharmony_ci					break;
44762306a36Sopenharmony_ci				/* Link ID */
44862306a36Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci				break;
45162306a36Sopenharmony_ci			case CFCTRL_SRV_UTIL:
45262306a36Sopenharmony_ci				/* Construct a frame, convert
45362306a36Sopenharmony_ci				 * DatagramConnectionID
45462306a36Sopenharmony_ci				 * to network format long and copy it out...
45562306a36Sopenharmony_ci				 */
45662306a36Sopenharmony_ci				/* Fifosize KB */
45762306a36Sopenharmony_ci				linkparam.u.utility.fifosize_kb =
45862306a36Sopenharmony_ci				    cfpkt_extr_head_u16(pkt);
45962306a36Sopenharmony_ci				/* Fifosize bufs */
46062306a36Sopenharmony_ci				linkparam.u.utility.fifosize_bufs =
46162306a36Sopenharmony_ci				    cfpkt_extr_head_u16(pkt);
46262306a36Sopenharmony_ci				/* name */
46362306a36Sopenharmony_ci				cp = (u8 *) linkparam.u.utility.name;
46462306a36Sopenharmony_ci				caif_assert(sizeof(linkparam.u.utility.name)
46562306a36Sopenharmony_ci					     >= UTILITY_NAME_LENGTH);
46662306a36Sopenharmony_ci				for (i = 0;
46762306a36Sopenharmony_ci				     i < UTILITY_NAME_LENGTH
46862306a36Sopenharmony_ci				     && cfpkt_more(pkt); i++) {
46962306a36Sopenharmony_ci					tmp = cfpkt_extr_head_u8(pkt);
47062306a36Sopenharmony_ci					*cp++ = tmp;
47162306a36Sopenharmony_ci				}
47262306a36Sopenharmony_ci				/* Length */
47362306a36Sopenharmony_ci				len = cfpkt_extr_head_u8(pkt);
47462306a36Sopenharmony_ci				linkparam.u.utility.paramlen = len;
47562306a36Sopenharmony_ci				/* Param Data */
47662306a36Sopenharmony_ci				cp = linkparam.u.utility.params;
47762306a36Sopenharmony_ci				while (cfpkt_more(pkt) && len--) {
47862306a36Sopenharmony_ci					tmp = cfpkt_extr_head_u8(pkt);
47962306a36Sopenharmony_ci					*cp++ = tmp;
48062306a36Sopenharmony_ci				}
48162306a36Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
48262306a36Sopenharmony_ci					break;
48362306a36Sopenharmony_ci				/* Link ID */
48462306a36Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
48562306a36Sopenharmony_ci				/* Length */
48662306a36Sopenharmony_ci				len = cfpkt_extr_head_u8(pkt);
48762306a36Sopenharmony_ci				/* Param Data */
48862306a36Sopenharmony_ci				cfpkt_extr_head(pkt, &param, len);
48962306a36Sopenharmony_ci				break;
49062306a36Sopenharmony_ci			default:
49162306a36Sopenharmony_ci				pr_warn("Request setup, invalid type (%d)\n",
49262306a36Sopenharmony_ci					serv);
49362306a36Sopenharmony_ci				goto error;
49462306a36Sopenharmony_ci			}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci			rsp.cmd = cmd;
49762306a36Sopenharmony_ci			rsp.param = linkparam;
49862306a36Sopenharmony_ci			spin_lock_bh(&cfctrl->info_list_lock);
49962306a36Sopenharmony_ci			req = cfctrl_remove_req(cfctrl, &rsp);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci			if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||
50262306a36Sopenharmony_ci				cfpkt_erroneous(pkt)) {
50362306a36Sopenharmony_ci				pr_err("Invalid O/E bit or parse error "
50462306a36Sopenharmony_ci						"on CAIF control channel\n");
50562306a36Sopenharmony_ci				cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
50662306a36Sopenharmony_ci						       0,
50762306a36Sopenharmony_ci						       req ? req->client_layer
50862306a36Sopenharmony_ci						       : NULL);
50962306a36Sopenharmony_ci			} else {
51062306a36Sopenharmony_ci				cfctrl->res.linksetup_rsp(cfctrl->serv.
51162306a36Sopenharmony_ci							  layer.up, linkid,
51262306a36Sopenharmony_ci							  serv, physlinkid,
51362306a36Sopenharmony_ci							  req ? req->
51462306a36Sopenharmony_ci							  client_layer : NULL);
51562306a36Sopenharmony_ci			}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci			kfree(req);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci			spin_unlock_bh(&cfctrl->info_list_lock);
52062306a36Sopenharmony_ci		}
52162306a36Sopenharmony_ci		break;
52262306a36Sopenharmony_ci	case CFCTRL_CMD_LINK_DESTROY:
52362306a36Sopenharmony_ci		linkid = cfpkt_extr_head_u8(pkt);
52462306a36Sopenharmony_ci		cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid);
52562306a36Sopenharmony_ci		break;
52662306a36Sopenharmony_ci	case CFCTRL_CMD_LINK_ERR:
52762306a36Sopenharmony_ci		pr_err("Frame Error Indication received\n");
52862306a36Sopenharmony_ci		cfctrl->res.linkerror_ind();
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci	case CFCTRL_CMD_ENUM:
53162306a36Sopenharmony_ci		cfctrl->res.enum_rsp();
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	case CFCTRL_CMD_SLEEP:
53462306a36Sopenharmony_ci		cfctrl->res.sleep_rsp();
53562306a36Sopenharmony_ci		break;
53662306a36Sopenharmony_ci	case CFCTRL_CMD_WAKE:
53762306a36Sopenharmony_ci		cfctrl->res.wake_rsp();
53862306a36Sopenharmony_ci		break;
53962306a36Sopenharmony_ci	case CFCTRL_CMD_LINK_RECONF:
54062306a36Sopenharmony_ci		cfctrl->res.restart_rsp();
54162306a36Sopenharmony_ci		break;
54262306a36Sopenharmony_ci	case CFCTRL_CMD_RADIO_SET:
54362306a36Sopenharmony_ci		cfctrl->res.radioset_rsp();
54462306a36Sopenharmony_ci		break;
54562306a36Sopenharmony_ci	default:
54662306a36Sopenharmony_ci		pr_err("Unrecognized Control Frame\n");
54762306a36Sopenharmony_ci		goto error;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci	ret = 0;
55062306a36Sopenharmony_cierror:
55162306a36Sopenharmony_ci	cfpkt_destroy(pkt);
55262306a36Sopenharmony_ci	return ret;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
55662306a36Sopenharmony_ci			   int phyid)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct cfctrl *this = container_obj(layr);
55962306a36Sopenharmony_ci	switch (ctrl) {
56062306a36Sopenharmony_ci	case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
56162306a36Sopenharmony_ci	case CAIF_CTRLCMD_FLOW_OFF_IND:
56262306a36Sopenharmony_ci		spin_lock_bh(&this->info_list_lock);
56362306a36Sopenharmony_ci		if (!list_empty(&this->list))
56462306a36Sopenharmony_ci			pr_debug("Received flow off in control layer\n");
56562306a36Sopenharmony_ci		spin_unlock_bh(&this->info_list_lock);
56662306a36Sopenharmony_ci		break;
56762306a36Sopenharmony_ci	case _CAIF_CTRLCMD_PHYIF_DOWN_IND: {
56862306a36Sopenharmony_ci		struct cfctrl_request_info *p, *tmp;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci		/* Find all connect request and report failure */
57162306a36Sopenharmony_ci		spin_lock_bh(&this->info_list_lock);
57262306a36Sopenharmony_ci		list_for_each_entry_safe(p, tmp, &this->list, list) {
57362306a36Sopenharmony_ci			if (p->param.phyid == phyid) {
57462306a36Sopenharmony_ci				list_del(&p->list);
57562306a36Sopenharmony_ci				p->client_layer->ctrlcmd(p->client_layer,
57662306a36Sopenharmony_ci						CAIF_CTRLCMD_INIT_FAIL_RSP,
57762306a36Sopenharmony_ci						phyid);
57862306a36Sopenharmony_ci				kfree(p);
57962306a36Sopenharmony_ci			}
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci		spin_unlock_bh(&this->info_list_lock);
58262306a36Sopenharmony_ci		break;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci	default:
58562306a36Sopenharmony_ci		break;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci#ifndef CAIF_NO_LOOP
59062306a36Sopenharmony_cistatic int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	static int last_linkid;
59362306a36Sopenharmony_ci	static int dec;
59462306a36Sopenharmony_ci	u8 linkid, linktype, tmp;
59562306a36Sopenharmony_ci	switch (cmd) {
59662306a36Sopenharmony_ci	case CFCTRL_CMD_LINK_SETUP:
59762306a36Sopenharmony_ci		spin_lock_bh(&ctrl->loop_linkid_lock);
59862306a36Sopenharmony_ci		if (!dec) {
59962306a36Sopenharmony_ci			for (linkid = last_linkid + 1; linkid < 254; linkid++)
60062306a36Sopenharmony_ci				if (!ctrl->loop_linkused[linkid])
60162306a36Sopenharmony_ci					goto found;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci		dec = 1;
60462306a36Sopenharmony_ci		for (linkid = last_linkid - 1; linkid > 1; linkid--)
60562306a36Sopenharmony_ci			if (!ctrl->loop_linkused[linkid])
60662306a36Sopenharmony_ci				goto found;
60762306a36Sopenharmony_ci		spin_unlock_bh(&ctrl->loop_linkid_lock);
60862306a36Sopenharmony_ci		return -1;
60962306a36Sopenharmony_cifound:
61062306a36Sopenharmony_ci		if (linkid < 10)
61162306a36Sopenharmony_ci			dec = 0;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		if (!ctrl->loop_linkused[linkid])
61462306a36Sopenharmony_ci			ctrl->loop_linkused[linkid] = 1;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci		last_linkid = linkid;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		cfpkt_add_trail(pkt, &linkid, 1);
61962306a36Sopenharmony_ci		spin_unlock_bh(&ctrl->loop_linkid_lock);
62062306a36Sopenharmony_ci		cfpkt_peek_head(pkt, &linktype, 1);
62162306a36Sopenharmony_ci		if (linktype ==  CFCTRL_SRV_UTIL) {
62262306a36Sopenharmony_ci			tmp = 0x01;
62362306a36Sopenharmony_ci			cfpkt_add_trail(pkt, &tmp, 1);
62462306a36Sopenharmony_ci			cfpkt_add_trail(pkt, &tmp, 1);
62562306a36Sopenharmony_ci		}
62662306a36Sopenharmony_ci		break;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	case CFCTRL_CMD_LINK_DESTROY:
62962306a36Sopenharmony_ci		spin_lock_bh(&ctrl->loop_linkid_lock);
63062306a36Sopenharmony_ci		cfpkt_peek_head(pkt, &linkid, 1);
63162306a36Sopenharmony_ci		ctrl->loop_linkused[linkid] = 0;
63262306a36Sopenharmony_ci		spin_unlock_bh(&ctrl->loop_linkid_lock);
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	default:
63562306a36Sopenharmony_ci		break;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci#endif
640