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, ¶m, 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