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/stddef.h>
108c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/pkt_sched.h>
138c2ecf20Sopenharmony_ci#include <net/caif/caif_layer.h>
148c2ecf20Sopenharmony_ci#include <net/caif/cfpkt.h>
158c2ecf20Sopenharmony_ci#include <net/caif/cfctrl.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define container_obj(layr) container_of(layr, struct cfctrl, serv.layer)
188c2ecf20Sopenharmony_ci#define UTILITY_NAME_LENGTH 16
198c2ecf20Sopenharmony_ci#define CFPKT_CTRL_PKT_LEN 20
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#ifdef CAIF_NO_LOOP
228c2ecf20Sopenharmony_cistatic int handle_loop(struct cfctrl *ctrl,
238c2ecf20Sopenharmony_ci		       int cmd, struct cfpkt *pkt){
248c2ecf20Sopenharmony_ci	return -1;
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_ci#else
278c2ecf20Sopenharmony_cistatic int handle_loop(struct cfctrl *ctrl,
288c2ecf20Sopenharmony_ci		       int cmd, struct cfpkt *pkt);
298c2ecf20Sopenharmony_ci#endif
308c2ecf20Sopenharmony_cistatic int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt);
318c2ecf20Sopenharmony_cistatic void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
328c2ecf20Sopenharmony_ci			   int phyid);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct cflayer *cfctrl_create(void)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct dev_info dev_info;
388c2ecf20Sopenharmony_ci	struct cfctrl *this =
398c2ecf20Sopenharmony_ci		kzalloc(sizeof(struct cfctrl), GFP_ATOMIC);
408c2ecf20Sopenharmony_ci	if (!this)
418c2ecf20Sopenharmony_ci		return NULL;
428c2ecf20Sopenharmony_ci	caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
438c2ecf20Sopenharmony_ci	memset(&dev_info, 0, sizeof(dev_info));
448c2ecf20Sopenharmony_ci	dev_info.id = 0xff;
458c2ecf20Sopenharmony_ci	cfsrvl_init(&this->serv, 0, &dev_info, false);
468c2ecf20Sopenharmony_ci	atomic_set(&this->req_seq_no, 1);
478c2ecf20Sopenharmony_ci	atomic_set(&this->rsp_seq_no, 1);
488c2ecf20Sopenharmony_ci	this->serv.layer.receive = cfctrl_recv;
498c2ecf20Sopenharmony_ci	sprintf(this->serv.layer.name, "ctrl");
508c2ecf20Sopenharmony_ci	this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
518c2ecf20Sopenharmony_ci#ifndef CAIF_NO_LOOP
528c2ecf20Sopenharmony_ci	spin_lock_init(&this->loop_linkid_lock);
538c2ecf20Sopenharmony_ci	this->loop_linkid = 1;
548c2ecf20Sopenharmony_ci#endif
558c2ecf20Sopenharmony_ci	spin_lock_init(&this->info_list_lock);
568c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&this->list);
578c2ecf20Sopenharmony_ci	return &this->serv.layer;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_civoid cfctrl_remove(struct cflayer *layer)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct cfctrl_request_info *p, *tmp;
638c2ecf20Sopenharmony_ci	struct cfctrl *ctrl = container_obj(layer);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	spin_lock_bh(&ctrl->info_list_lock);
668c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
678c2ecf20Sopenharmony_ci		list_del(&p->list);
688c2ecf20Sopenharmony_ci		kfree(p);
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctrl->info_list_lock);
718c2ecf20Sopenharmony_ci	kfree(layer);
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic bool param_eq(const struct cfctrl_link_param *p1,
758c2ecf20Sopenharmony_ci		     const struct cfctrl_link_param *p2)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	bool eq =
788c2ecf20Sopenharmony_ci	    p1->linktype == p2->linktype &&
798c2ecf20Sopenharmony_ci	    p1->priority == p2->priority &&
808c2ecf20Sopenharmony_ci	    p1->phyid == p2->phyid &&
818c2ecf20Sopenharmony_ci	    p1->endpoint == p2->endpoint && p1->chtype == p2->chtype;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (!eq)
848c2ecf20Sopenharmony_ci		return false;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	switch (p1->linktype) {
878c2ecf20Sopenharmony_ci	case CFCTRL_SRV_VEI:
888c2ecf20Sopenharmony_ci		return true;
898c2ecf20Sopenharmony_ci	case CFCTRL_SRV_DATAGRAM:
908c2ecf20Sopenharmony_ci		return p1->u.datagram.connid == p2->u.datagram.connid;
918c2ecf20Sopenharmony_ci	case CFCTRL_SRV_RFM:
928c2ecf20Sopenharmony_ci		return
938c2ecf20Sopenharmony_ci		    p1->u.rfm.connid == p2->u.rfm.connid &&
948c2ecf20Sopenharmony_ci		    strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0;
958c2ecf20Sopenharmony_ci	case CFCTRL_SRV_UTIL:
968c2ecf20Sopenharmony_ci		return
978c2ecf20Sopenharmony_ci		    p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb
988c2ecf20Sopenharmony_ci		    && p1->u.utility.fifosize_bufs ==
998c2ecf20Sopenharmony_ci		    p2->u.utility.fifosize_bufs
1008c2ecf20Sopenharmony_ci		    && strcmp(p1->u.utility.name, p2->u.utility.name) == 0
1018c2ecf20Sopenharmony_ci		    && p1->u.utility.paramlen == p2->u.utility.paramlen
1028c2ecf20Sopenharmony_ci		    && memcmp(p1->u.utility.params, p2->u.utility.params,
1038c2ecf20Sopenharmony_ci			      p1->u.utility.paramlen) == 0;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	case CFCTRL_SRV_VIDEO:
1068c2ecf20Sopenharmony_ci		return p1->u.video.connid == p2->u.video.connid;
1078c2ecf20Sopenharmony_ci	case CFCTRL_SRV_DBG:
1088c2ecf20Sopenharmony_ci		return true;
1098c2ecf20Sopenharmony_ci	case CFCTRL_SRV_DECM:
1108c2ecf20Sopenharmony_ci		return false;
1118c2ecf20Sopenharmony_ci	default:
1128c2ecf20Sopenharmony_ci		return false;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	return false;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic bool cfctrl_req_eq(const struct cfctrl_request_info *r1,
1188c2ecf20Sopenharmony_ci			  const struct cfctrl_request_info *r2)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	if (r1->cmd != r2->cmd)
1218c2ecf20Sopenharmony_ci		return false;
1228c2ecf20Sopenharmony_ci	if (r1->cmd == CFCTRL_CMD_LINK_SETUP)
1238c2ecf20Sopenharmony_ci		return param_eq(&r1->param, &r2->param);
1248c2ecf20Sopenharmony_ci	else
1258c2ecf20Sopenharmony_ci		return r1->channel_id == r2->channel_id;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/* Insert request at the end */
1298c2ecf20Sopenharmony_cistatic void cfctrl_insert_req(struct cfctrl *ctrl,
1308c2ecf20Sopenharmony_ci			      struct cfctrl_request_info *req)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	spin_lock_bh(&ctrl->info_list_lock);
1338c2ecf20Sopenharmony_ci	atomic_inc(&ctrl->req_seq_no);
1348c2ecf20Sopenharmony_ci	req->sequence_no = atomic_read(&ctrl->req_seq_no);
1358c2ecf20Sopenharmony_ci	list_add_tail(&req->list, &ctrl->list);
1368c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctrl->info_list_lock);
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci/* Compare and remove request */
1408c2ecf20Sopenharmony_cistatic struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
1418c2ecf20Sopenharmony_ci						struct cfctrl_request_info *req)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	struct cfctrl_request_info *p, *tmp, *first;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
1488c2ecf20Sopenharmony_ci		if (cfctrl_req_eq(req, p)) {
1498c2ecf20Sopenharmony_ci			if (p != first)
1508c2ecf20Sopenharmony_ci				pr_warn("Requests are not received in order\n");
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci			atomic_set(&ctrl->rsp_seq_no,
1538c2ecf20Sopenharmony_ci					 p->sequence_no);
1548c2ecf20Sopenharmony_ci			list_del(&p->list);
1558c2ecf20Sopenharmony_ci			goto out;
1568c2ecf20Sopenharmony_ci		}
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	p = NULL;
1598c2ecf20Sopenharmony_ciout:
1608c2ecf20Sopenharmony_ci	return p;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistruct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct cfctrl *this = container_obj(layer);
1668c2ecf20Sopenharmony_ci	return &this->res;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	info->hdr_len = 0;
1728c2ecf20Sopenharmony_ci	info->channel_id = cfctrl->serv.layer.id;
1738c2ecf20Sopenharmony_ci	info->dev_info = &cfctrl->serv.dev_info;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_civoid cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct cfpkt *pkt;
1798c2ecf20Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
1808c2ecf20Sopenharmony_ci	struct cflayer *dn = cfctrl->serv.layer.dn;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (!dn) {
1838c2ecf20Sopenharmony_ci		pr_debug("not able to send enum request\n");
1848c2ecf20Sopenharmony_ci		return;
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
1878c2ecf20Sopenharmony_ci	if (!pkt)
1888c2ecf20Sopenharmony_ci		return;
1898c2ecf20Sopenharmony_ci	caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
1908c2ecf20Sopenharmony_ci	init_info(cfpkt_info(pkt), cfctrl);
1918c2ecf20Sopenharmony_ci	cfpkt_info(pkt)->dev_info->id = physlinkid;
1928c2ecf20Sopenharmony_ci	cfctrl->serv.dev_info.id = physlinkid;
1938c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
1948c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, physlinkid);
1958c2ecf20Sopenharmony_ci	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
1968c2ecf20Sopenharmony_ci	dn->transmit(dn, pkt);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ciint cfctrl_linkup_request(struct cflayer *layer,
2008c2ecf20Sopenharmony_ci			  struct cfctrl_link_param *param,
2018c2ecf20Sopenharmony_ci			  struct cflayer *user_layer)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
2048c2ecf20Sopenharmony_ci	u32 tmp32;
2058c2ecf20Sopenharmony_ci	u16 tmp16;
2068c2ecf20Sopenharmony_ci	u8 tmp8;
2078c2ecf20Sopenharmony_ci	struct cfctrl_request_info *req;
2088c2ecf20Sopenharmony_ci	int ret;
2098c2ecf20Sopenharmony_ci	char utility_name[16];
2108c2ecf20Sopenharmony_ci	struct cfpkt *pkt;
2118c2ecf20Sopenharmony_ci	struct cflayer *dn = cfctrl->serv.layer.dn;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (!dn) {
2148c2ecf20Sopenharmony_ci		pr_debug("not able to send linkup request\n");
2158c2ecf20Sopenharmony_ci		return -ENODEV;
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (cfctrl_cancel_req(layer, user_layer) > 0) {
2198c2ecf20Sopenharmony_ci		/* Slight Paranoia, check if already connecting */
2208c2ecf20Sopenharmony_ci		pr_err("Duplicate connect request for same client\n");
2218c2ecf20Sopenharmony_ci		WARN_ON(1);
2228c2ecf20Sopenharmony_ci		return -EALREADY;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
2268c2ecf20Sopenharmony_ci	if (!pkt)
2278c2ecf20Sopenharmony_ci		return -ENOMEM;
2288c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
2298c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype);
2308c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid);
2318c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, param->endpoint & 0x03);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	switch (param->linktype) {
2348c2ecf20Sopenharmony_ci	case CFCTRL_SRV_VEI:
2358c2ecf20Sopenharmony_ci		break;
2368c2ecf20Sopenharmony_ci	case CFCTRL_SRV_VIDEO:
2378c2ecf20Sopenharmony_ci		cfpkt_addbdy(pkt, (u8) param->u.video.connid);
2388c2ecf20Sopenharmony_ci		break;
2398c2ecf20Sopenharmony_ci	case CFCTRL_SRV_DBG:
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case CFCTRL_SRV_DATAGRAM:
2428c2ecf20Sopenharmony_ci		tmp32 = cpu_to_le32(param->u.datagram.connid);
2438c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, &tmp32, 4);
2448c2ecf20Sopenharmony_ci		break;
2458c2ecf20Sopenharmony_ci	case CFCTRL_SRV_RFM:
2468c2ecf20Sopenharmony_ci		/* Construct a frame, convert DatagramConnectionID to network
2478c2ecf20Sopenharmony_ci		 * format long and copy it out...
2488c2ecf20Sopenharmony_ci		 */
2498c2ecf20Sopenharmony_ci		tmp32 = cpu_to_le32(param->u.rfm.connid);
2508c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, &tmp32, 4);
2518c2ecf20Sopenharmony_ci		/* Add volume name, including zero termination... */
2528c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, param->u.rfm.volume,
2538c2ecf20Sopenharmony_ci			       strlen(param->u.rfm.volume) + 1);
2548c2ecf20Sopenharmony_ci		break;
2558c2ecf20Sopenharmony_ci	case CFCTRL_SRV_UTIL:
2568c2ecf20Sopenharmony_ci		tmp16 = cpu_to_le16(param->u.utility.fifosize_kb);
2578c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, &tmp16, 2);
2588c2ecf20Sopenharmony_ci		tmp16 = cpu_to_le16(param->u.utility.fifosize_bufs);
2598c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, &tmp16, 2);
2608c2ecf20Sopenharmony_ci		memset(utility_name, 0, sizeof(utility_name));
2618c2ecf20Sopenharmony_ci		strlcpy(utility_name, param->u.utility.name,
2628c2ecf20Sopenharmony_ci			UTILITY_NAME_LENGTH);
2638c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH);
2648c2ecf20Sopenharmony_ci		tmp8 = param->u.utility.paramlen;
2658c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, &tmp8, 1);
2668c2ecf20Sopenharmony_ci		cfpkt_add_body(pkt, param->u.utility.params,
2678c2ecf20Sopenharmony_ci			       param->u.utility.paramlen);
2688c2ecf20Sopenharmony_ci		break;
2698c2ecf20Sopenharmony_ci	default:
2708c2ecf20Sopenharmony_ci		pr_warn("Request setup of bad link type = %d\n",
2718c2ecf20Sopenharmony_ci			param->linktype);
2728c2ecf20Sopenharmony_ci		cfpkt_destroy(pkt);
2738c2ecf20Sopenharmony_ci		return -EINVAL;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
2768c2ecf20Sopenharmony_ci	if (!req) {
2778c2ecf20Sopenharmony_ci		cfpkt_destroy(pkt);
2788c2ecf20Sopenharmony_ci		return -ENOMEM;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	req->client_layer = user_layer;
2828c2ecf20Sopenharmony_ci	req->cmd = CFCTRL_CMD_LINK_SETUP;
2838c2ecf20Sopenharmony_ci	req->param = *param;
2848c2ecf20Sopenharmony_ci	cfctrl_insert_req(cfctrl, req);
2858c2ecf20Sopenharmony_ci	init_info(cfpkt_info(pkt), cfctrl);
2868c2ecf20Sopenharmony_ci	/*
2878c2ecf20Sopenharmony_ci	 * NOTE:Always send linkup and linkdown request on the same
2888c2ecf20Sopenharmony_ci	 *	device as the payload. Otherwise old queued up payload
2898c2ecf20Sopenharmony_ci	 *	might arrive with the newly allocated channel ID.
2908c2ecf20Sopenharmony_ci	 */
2918c2ecf20Sopenharmony_ci	cfpkt_info(pkt)->dev_info->id = param->phyid;
2928c2ecf20Sopenharmony_ci	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
2938c2ecf20Sopenharmony_ci	ret =
2948c2ecf20Sopenharmony_ci	    dn->transmit(dn, pkt);
2958c2ecf20Sopenharmony_ci	if (ret < 0) {
2968c2ecf20Sopenharmony_ci		int count;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		count = cfctrl_cancel_req(&cfctrl->serv.layer,
2998c2ecf20Sopenharmony_ci						user_layer);
3008c2ecf20Sopenharmony_ci		if (count != 1) {
3018c2ecf20Sopenharmony_ci			pr_err("Could not remove request (%d)", count);
3028c2ecf20Sopenharmony_ci			return -ENODEV;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci	return 0;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ciint cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
3098c2ecf20Sopenharmony_ci			struct cflayer *client)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	int ret;
3128c2ecf20Sopenharmony_ci	struct cfpkt *pkt;
3138c2ecf20Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
3148c2ecf20Sopenharmony_ci	struct cflayer *dn = cfctrl->serv.layer.dn;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!dn) {
3178c2ecf20Sopenharmony_ci		pr_debug("not able to send link-down request\n");
3188c2ecf20Sopenharmony_ci		return -ENODEV;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
3218c2ecf20Sopenharmony_ci	if (!pkt)
3228c2ecf20Sopenharmony_ci		return -ENOMEM;
3238c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
3248c2ecf20Sopenharmony_ci	cfpkt_addbdy(pkt, channelid);
3258c2ecf20Sopenharmony_ci	init_info(cfpkt_info(pkt), cfctrl);
3268c2ecf20Sopenharmony_ci	cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
3278c2ecf20Sopenharmony_ci	ret =
3288c2ecf20Sopenharmony_ci	    dn->transmit(dn, pkt);
3298c2ecf20Sopenharmony_ci#ifndef CAIF_NO_LOOP
3308c2ecf20Sopenharmony_ci	cfctrl->loop_linkused[channelid] = 0;
3318c2ecf20Sopenharmony_ci#endif
3328c2ecf20Sopenharmony_ci	return ret;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ciint cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	struct cfctrl_request_info *p, *tmp;
3388c2ecf20Sopenharmony_ci	struct cfctrl *ctrl = container_obj(layr);
3398c2ecf20Sopenharmony_ci	int found = 0;
3408c2ecf20Sopenharmony_ci	spin_lock_bh(&ctrl->info_list_lock);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
3438c2ecf20Sopenharmony_ci		if (p->client_layer == adap_layer) {
3448c2ecf20Sopenharmony_ci			list_del(&p->list);
3458c2ecf20Sopenharmony_ci			kfree(p);
3468c2ecf20Sopenharmony_ci			found++;
3478c2ecf20Sopenharmony_ci		}
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	spin_unlock_bh(&ctrl->info_list_lock);
3518c2ecf20Sopenharmony_ci	return found;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	u8 cmdrsp;
3578c2ecf20Sopenharmony_ci	u8 cmd;
3588c2ecf20Sopenharmony_ci	int ret = -1;
3598c2ecf20Sopenharmony_ci	u8 len;
3608c2ecf20Sopenharmony_ci	u8 param[255];
3618c2ecf20Sopenharmony_ci	u8 linkid = 0;
3628c2ecf20Sopenharmony_ci	struct cfctrl *cfctrl = container_obj(layer);
3638c2ecf20Sopenharmony_ci	struct cfctrl_request_info rsp, *req;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	cmdrsp = cfpkt_extr_head_u8(pkt);
3678c2ecf20Sopenharmony_ci	cmd = cmdrsp & CFCTRL_CMD_MASK;
3688c2ecf20Sopenharmony_ci	if (cmd != CFCTRL_CMD_LINK_ERR
3698c2ecf20Sopenharmony_ci	    && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)
3708c2ecf20Sopenharmony_ci		&& CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) {
3718c2ecf20Sopenharmony_ci		if (handle_loop(cfctrl, cmd, pkt) != 0)
3728c2ecf20Sopenharmony_ci			cmdrsp |= CFCTRL_ERR_BIT;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	switch (cmd) {
3768c2ecf20Sopenharmony_ci	case CFCTRL_CMD_LINK_SETUP:
3778c2ecf20Sopenharmony_ci		{
3788c2ecf20Sopenharmony_ci			enum cfctrl_srv serv;
3798c2ecf20Sopenharmony_ci			enum cfctrl_srv servtype;
3808c2ecf20Sopenharmony_ci			u8 endpoint;
3818c2ecf20Sopenharmony_ci			u8 physlinkid;
3828c2ecf20Sopenharmony_ci			u8 prio;
3838c2ecf20Sopenharmony_ci			u8 tmp;
3848c2ecf20Sopenharmony_ci			u8 *cp;
3858c2ecf20Sopenharmony_ci			int i;
3868c2ecf20Sopenharmony_ci			struct cfctrl_link_param linkparam;
3878c2ecf20Sopenharmony_ci			memset(&linkparam, 0, sizeof(linkparam));
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci			tmp = cfpkt_extr_head_u8(pkt);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci			serv = tmp & CFCTRL_SRV_MASK;
3928c2ecf20Sopenharmony_ci			linkparam.linktype = serv;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci			servtype = tmp >> 4;
3958c2ecf20Sopenharmony_ci			linkparam.chtype = servtype;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci			tmp = cfpkt_extr_head_u8(pkt);
3988c2ecf20Sopenharmony_ci			physlinkid = tmp & 0x07;
3998c2ecf20Sopenharmony_ci			prio = tmp >> 3;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci			linkparam.priority = prio;
4028c2ecf20Sopenharmony_ci			linkparam.phyid = physlinkid;
4038c2ecf20Sopenharmony_ci			endpoint = cfpkt_extr_head_u8(pkt);
4048c2ecf20Sopenharmony_ci			linkparam.endpoint = endpoint & 0x03;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci			switch (serv) {
4078c2ecf20Sopenharmony_ci			case CFCTRL_SRV_VEI:
4088c2ecf20Sopenharmony_ci			case CFCTRL_SRV_DBG:
4098c2ecf20Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
4108c2ecf20Sopenharmony_ci					break;
4118c2ecf20Sopenharmony_ci				/* Link ID */
4128c2ecf20Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
4138c2ecf20Sopenharmony_ci				break;
4148c2ecf20Sopenharmony_ci			case CFCTRL_SRV_VIDEO:
4158c2ecf20Sopenharmony_ci				tmp = cfpkt_extr_head_u8(pkt);
4168c2ecf20Sopenharmony_ci				linkparam.u.video.connid = tmp;
4178c2ecf20Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
4188c2ecf20Sopenharmony_ci					break;
4198c2ecf20Sopenharmony_ci				/* Link ID */
4208c2ecf20Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
4218c2ecf20Sopenharmony_ci				break;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci			case CFCTRL_SRV_DATAGRAM:
4248c2ecf20Sopenharmony_ci				linkparam.u.datagram.connid =
4258c2ecf20Sopenharmony_ci				    cfpkt_extr_head_u32(pkt);
4268c2ecf20Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
4278c2ecf20Sopenharmony_ci					break;
4288c2ecf20Sopenharmony_ci				/* Link ID */
4298c2ecf20Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
4308c2ecf20Sopenharmony_ci				break;
4318c2ecf20Sopenharmony_ci			case CFCTRL_SRV_RFM:
4328c2ecf20Sopenharmony_ci				/* Construct a frame, convert
4338c2ecf20Sopenharmony_ci				 * DatagramConnectionID
4348c2ecf20Sopenharmony_ci				 * to network format long and copy it out...
4358c2ecf20Sopenharmony_ci				 */
4368c2ecf20Sopenharmony_ci				linkparam.u.rfm.connid =
4378c2ecf20Sopenharmony_ci				    cfpkt_extr_head_u32(pkt);
4388c2ecf20Sopenharmony_ci				cp = (u8 *) linkparam.u.rfm.volume;
4398c2ecf20Sopenharmony_ci				for (tmp = cfpkt_extr_head_u8(pkt);
4408c2ecf20Sopenharmony_ci				     cfpkt_more(pkt) && tmp != '\0';
4418c2ecf20Sopenharmony_ci				     tmp = cfpkt_extr_head_u8(pkt))
4428c2ecf20Sopenharmony_ci					*cp++ = tmp;
4438c2ecf20Sopenharmony_ci				*cp = '\0';
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
4468c2ecf20Sopenharmony_ci					break;
4478c2ecf20Sopenharmony_ci				/* Link ID */
4488c2ecf20Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci				break;
4518c2ecf20Sopenharmony_ci			case CFCTRL_SRV_UTIL:
4528c2ecf20Sopenharmony_ci				/* Construct a frame, convert
4538c2ecf20Sopenharmony_ci				 * DatagramConnectionID
4548c2ecf20Sopenharmony_ci				 * to network format long and copy it out...
4558c2ecf20Sopenharmony_ci				 */
4568c2ecf20Sopenharmony_ci				/* Fifosize KB */
4578c2ecf20Sopenharmony_ci				linkparam.u.utility.fifosize_kb =
4588c2ecf20Sopenharmony_ci				    cfpkt_extr_head_u16(pkt);
4598c2ecf20Sopenharmony_ci				/* Fifosize bufs */
4608c2ecf20Sopenharmony_ci				linkparam.u.utility.fifosize_bufs =
4618c2ecf20Sopenharmony_ci				    cfpkt_extr_head_u16(pkt);
4628c2ecf20Sopenharmony_ci				/* name */
4638c2ecf20Sopenharmony_ci				cp = (u8 *) linkparam.u.utility.name;
4648c2ecf20Sopenharmony_ci				caif_assert(sizeof(linkparam.u.utility.name)
4658c2ecf20Sopenharmony_ci					     >= UTILITY_NAME_LENGTH);
4668c2ecf20Sopenharmony_ci				for (i = 0;
4678c2ecf20Sopenharmony_ci				     i < UTILITY_NAME_LENGTH
4688c2ecf20Sopenharmony_ci				     && cfpkt_more(pkt); i++) {
4698c2ecf20Sopenharmony_ci					tmp = cfpkt_extr_head_u8(pkt);
4708c2ecf20Sopenharmony_ci					*cp++ = tmp;
4718c2ecf20Sopenharmony_ci				}
4728c2ecf20Sopenharmony_ci				/* Length */
4738c2ecf20Sopenharmony_ci				len = cfpkt_extr_head_u8(pkt);
4748c2ecf20Sopenharmony_ci				linkparam.u.utility.paramlen = len;
4758c2ecf20Sopenharmony_ci				/* Param Data */
4768c2ecf20Sopenharmony_ci				cp = linkparam.u.utility.params;
4778c2ecf20Sopenharmony_ci				while (cfpkt_more(pkt) && len--) {
4788c2ecf20Sopenharmony_ci					tmp = cfpkt_extr_head_u8(pkt);
4798c2ecf20Sopenharmony_ci					*cp++ = tmp;
4808c2ecf20Sopenharmony_ci				}
4818c2ecf20Sopenharmony_ci				if (CFCTRL_ERR_BIT & cmdrsp)
4828c2ecf20Sopenharmony_ci					break;
4838c2ecf20Sopenharmony_ci				/* Link ID */
4848c2ecf20Sopenharmony_ci				linkid = cfpkt_extr_head_u8(pkt);
4858c2ecf20Sopenharmony_ci				/* Length */
4868c2ecf20Sopenharmony_ci				len = cfpkt_extr_head_u8(pkt);
4878c2ecf20Sopenharmony_ci				/* Param Data */
4888c2ecf20Sopenharmony_ci				cfpkt_extr_head(pkt, &param, len);
4898c2ecf20Sopenharmony_ci				break;
4908c2ecf20Sopenharmony_ci			default:
4918c2ecf20Sopenharmony_ci				pr_warn("Request setup, invalid type (%d)\n",
4928c2ecf20Sopenharmony_ci					serv);
4938c2ecf20Sopenharmony_ci				goto error;
4948c2ecf20Sopenharmony_ci			}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci			rsp.cmd = cmd;
4978c2ecf20Sopenharmony_ci			rsp.param = linkparam;
4988c2ecf20Sopenharmony_ci			spin_lock_bh(&cfctrl->info_list_lock);
4998c2ecf20Sopenharmony_ci			req = cfctrl_remove_req(cfctrl, &rsp);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci			if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||
5028c2ecf20Sopenharmony_ci				cfpkt_erroneous(pkt)) {
5038c2ecf20Sopenharmony_ci				pr_err("Invalid O/E bit or parse error "
5048c2ecf20Sopenharmony_ci						"on CAIF control channel\n");
5058c2ecf20Sopenharmony_ci				cfctrl->res.reject_rsp(cfctrl->serv.layer.up,
5068c2ecf20Sopenharmony_ci						       0,
5078c2ecf20Sopenharmony_ci						       req ? req->client_layer
5088c2ecf20Sopenharmony_ci						       : NULL);
5098c2ecf20Sopenharmony_ci			} else {
5108c2ecf20Sopenharmony_ci				cfctrl->res.linksetup_rsp(cfctrl->serv.
5118c2ecf20Sopenharmony_ci							  layer.up, linkid,
5128c2ecf20Sopenharmony_ci							  serv, physlinkid,
5138c2ecf20Sopenharmony_ci							  req ? req->
5148c2ecf20Sopenharmony_ci							  client_layer : NULL);
5158c2ecf20Sopenharmony_ci			}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci			kfree(req);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci			spin_unlock_bh(&cfctrl->info_list_lock);
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci		break;
5228c2ecf20Sopenharmony_ci	case CFCTRL_CMD_LINK_DESTROY:
5238c2ecf20Sopenharmony_ci		linkid = cfpkt_extr_head_u8(pkt);
5248c2ecf20Sopenharmony_ci		cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid);
5258c2ecf20Sopenharmony_ci		break;
5268c2ecf20Sopenharmony_ci	case CFCTRL_CMD_LINK_ERR:
5278c2ecf20Sopenharmony_ci		pr_err("Frame Error Indication received\n");
5288c2ecf20Sopenharmony_ci		cfctrl->res.linkerror_ind();
5298c2ecf20Sopenharmony_ci		break;
5308c2ecf20Sopenharmony_ci	case CFCTRL_CMD_ENUM:
5318c2ecf20Sopenharmony_ci		cfctrl->res.enum_rsp();
5328c2ecf20Sopenharmony_ci		break;
5338c2ecf20Sopenharmony_ci	case CFCTRL_CMD_SLEEP:
5348c2ecf20Sopenharmony_ci		cfctrl->res.sleep_rsp();
5358c2ecf20Sopenharmony_ci		break;
5368c2ecf20Sopenharmony_ci	case CFCTRL_CMD_WAKE:
5378c2ecf20Sopenharmony_ci		cfctrl->res.wake_rsp();
5388c2ecf20Sopenharmony_ci		break;
5398c2ecf20Sopenharmony_ci	case CFCTRL_CMD_LINK_RECONF:
5408c2ecf20Sopenharmony_ci		cfctrl->res.restart_rsp();
5418c2ecf20Sopenharmony_ci		break;
5428c2ecf20Sopenharmony_ci	case CFCTRL_CMD_RADIO_SET:
5438c2ecf20Sopenharmony_ci		cfctrl->res.radioset_rsp();
5448c2ecf20Sopenharmony_ci		break;
5458c2ecf20Sopenharmony_ci	default:
5468c2ecf20Sopenharmony_ci		pr_err("Unrecognized Control Frame\n");
5478c2ecf20Sopenharmony_ci		goto error;
5488c2ecf20Sopenharmony_ci	}
5498c2ecf20Sopenharmony_ci	ret = 0;
5508c2ecf20Sopenharmony_cierror:
5518c2ecf20Sopenharmony_ci	cfpkt_destroy(pkt);
5528c2ecf20Sopenharmony_ci	return ret;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
5568c2ecf20Sopenharmony_ci			   int phyid)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	struct cfctrl *this = container_obj(layr);
5598c2ecf20Sopenharmony_ci	switch (ctrl) {
5608c2ecf20Sopenharmony_ci	case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
5618c2ecf20Sopenharmony_ci	case CAIF_CTRLCMD_FLOW_OFF_IND:
5628c2ecf20Sopenharmony_ci		spin_lock_bh(&this->info_list_lock);
5638c2ecf20Sopenharmony_ci		if (!list_empty(&this->list))
5648c2ecf20Sopenharmony_ci			pr_debug("Received flow off in control layer\n");
5658c2ecf20Sopenharmony_ci		spin_unlock_bh(&this->info_list_lock);
5668c2ecf20Sopenharmony_ci		break;
5678c2ecf20Sopenharmony_ci	case _CAIF_CTRLCMD_PHYIF_DOWN_IND: {
5688c2ecf20Sopenharmony_ci		struct cfctrl_request_info *p, *tmp;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci		/* Find all connect request and report failure */
5718c2ecf20Sopenharmony_ci		spin_lock_bh(&this->info_list_lock);
5728c2ecf20Sopenharmony_ci		list_for_each_entry_safe(p, tmp, &this->list, list) {
5738c2ecf20Sopenharmony_ci			if (p->param.phyid == phyid) {
5748c2ecf20Sopenharmony_ci				list_del(&p->list);
5758c2ecf20Sopenharmony_ci				p->client_layer->ctrlcmd(p->client_layer,
5768c2ecf20Sopenharmony_ci						CAIF_CTRLCMD_INIT_FAIL_RSP,
5778c2ecf20Sopenharmony_ci						phyid);
5788c2ecf20Sopenharmony_ci				kfree(p);
5798c2ecf20Sopenharmony_ci			}
5808c2ecf20Sopenharmony_ci		}
5818c2ecf20Sopenharmony_ci		spin_unlock_bh(&this->info_list_lock);
5828c2ecf20Sopenharmony_ci		break;
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci	default:
5858c2ecf20Sopenharmony_ci		break;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci#ifndef CAIF_NO_LOOP
5908c2ecf20Sopenharmony_cistatic int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	static int last_linkid;
5938c2ecf20Sopenharmony_ci	static int dec;
5948c2ecf20Sopenharmony_ci	u8 linkid, linktype, tmp;
5958c2ecf20Sopenharmony_ci	switch (cmd) {
5968c2ecf20Sopenharmony_ci	case CFCTRL_CMD_LINK_SETUP:
5978c2ecf20Sopenharmony_ci		spin_lock_bh(&ctrl->loop_linkid_lock);
5988c2ecf20Sopenharmony_ci		if (!dec) {
5998c2ecf20Sopenharmony_ci			for (linkid = last_linkid + 1; linkid < 254; linkid++)
6008c2ecf20Sopenharmony_ci				if (!ctrl->loop_linkused[linkid])
6018c2ecf20Sopenharmony_ci					goto found;
6028c2ecf20Sopenharmony_ci		}
6038c2ecf20Sopenharmony_ci		dec = 1;
6048c2ecf20Sopenharmony_ci		for (linkid = last_linkid - 1; linkid > 1; linkid--)
6058c2ecf20Sopenharmony_ci			if (!ctrl->loop_linkused[linkid])
6068c2ecf20Sopenharmony_ci				goto found;
6078c2ecf20Sopenharmony_ci		spin_unlock_bh(&ctrl->loop_linkid_lock);
6088c2ecf20Sopenharmony_ci		return -1;
6098c2ecf20Sopenharmony_cifound:
6108c2ecf20Sopenharmony_ci		if (linkid < 10)
6118c2ecf20Sopenharmony_ci			dec = 0;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		if (!ctrl->loop_linkused[linkid])
6148c2ecf20Sopenharmony_ci			ctrl->loop_linkused[linkid] = 1;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		last_linkid = linkid;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci		cfpkt_add_trail(pkt, &linkid, 1);
6198c2ecf20Sopenharmony_ci		spin_unlock_bh(&ctrl->loop_linkid_lock);
6208c2ecf20Sopenharmony_ci		cfpkt_peek_head(pkt, &linktype, 1);
6218c2ecf20Sopenharmony_ci		if (linktype ==  CFCTRL_SRV_UTIL) {
6228c2ecf20Sopenharmony_ci			tmp = 0x01;
6238c2ecf20Sopenharmony_ci			cfpkt_add_trail(pkt, &tmp, 1);
6248c2ecf20Sopenharmony_ci			cfpkt_add_trail(pkt, &tmp, 1);
6258c2ecf20Sopenharmony_ci		}
6268c2ecf20Sopenharmony_ci		break;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	case CFCTRL_CMD_LINK_DESTROY:
6298c2ecf20Sopenharmony_ci		spin_lock_bh(&ctrl->loop_linkid_lock);
6308c2ecf20Sopenharmony_ci		cfpkt_peek_head(pkt, &linkid, 1);
6318c2ecf20Sopenharmony_ci		ctrl->loop_linkused[linkid] = 0;
6328c2ecf20Sopenharmony_ci		spin_unlock_bh(&ctrl->loop_linkid_lock);
6338c2ecf20Sopenharmony_ci		break;
6348c2ecf20Sopenharmony_ci	default:
6358c2ecf20Sopenharmony_ci		break;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci	return 0;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci#endif
640