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