blob: 4ed490bbb55587275dfbb73e6427d6b219694ed2 [file] [log] [blame]
/*
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/******************************************************************************
* wlan_ptt_sock_svc.c
*
******************************************************************************/
#ifdef PTT_SOCK_SVC_ENABLE
#include <wlan_nlink_srv.h>
#include <halTypes.h>
#include <vos_status.h>
#include <wlan_hdd_includes.h>
#include <vos_trace.h>
#include <wlan_nlink_common.h>
#include <wlan_ptt_sock_svc.h>
#include <vos_types.h>
#include <vos_trace.h>
#ifdef ANI_MANF_DIAG
#include <wlan_hdd_ftm.h>
#endif
#define PTT_SOCK_DEBUG
#ifdef PTT_SOCK_DEBUG
#define PTT_TRACE(level, args...) VOS_TRACE( VOS_MODULE_ID_HDD, level, ## args)
#else
#define PTT_TRACE(level, args...)
#endif
// Global variables
static struct hdd_context_s *pAdapterHandle;
//Utility function to perform endianess swap
static void ptt_sock_swap_32(void *pBuffer, unsigned int len)
{
v_U32_t *pBuf32, data;
v_U8_t *pBuf8;
unsigned int i;
len &= ~(sizeof(v_U32_t)-1);
pBuf32 = (v_U32_t *) pBuffer;
pBuf8 = (v_U8_t *) pBuffer;
for (i = 0; i < len; i += 4, ++pBuf32, pBuf8 += 4) {
data = *pBuf32;
pBuf8[0] = (v_U8_t) ((data >> 24) & 0xff);
pBuf8[1] = (v_U8_t) ((data >> 16) & 0xff);
pBuf8[2] = (v_U8_t) ((data >> 8) & 0xff);
pBuf8[3] = (v_U8_t) ((data >> 0) & 0xff);
}
}
#ifdef PTT_SOCK_DEBUG_VERBOSE
//Utility function to perform a hex dump
static void ptt_sock_dump_buf(const unsigned char * pbuf, int cnt)
{
int i;
for (i = 0; i < cnt ; i++) {
if ((i%16)==0)
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"\n%p:", pbuf);
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO," %02X", *pbuf);
pbuf++;
}
VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,"\n");
}
#endif
//Utility function to send a netlink message to an application in user space
int ptt_sock_send_msg_to_app(tAniHdr *wmsg, int radio, int src_mod, int pid)
{
int err = -1;
int payload_len;
int tot_msg_len;
tAniNlHdr *wnl;
struct sk_buff *skb;
struct nlmsghdr *nlh;
int wmsg_length = be16_to_cpu(wmsg->length);
static int nlmsg_seq;
if (radio < 0 || radio > ANI_MAX_RADIOS) {
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: invalid radio id [%d]\n",
__func__, radio);
return -EINVAL;
}
payload_len = wmsg_length + 4; // 4 extra bytes for the radio idx
tot_msg_len = NLMSG_SPACE(payload_len);
if ((skb = dev_alloc_skb(tot_msg_len)) == NULL) {
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: dev_alloc_skb() failed for msg size[%d]\n",
__func__, tot_msg_len);
return -ENOMEM;
}
nlh = NLMSG_PUT(skb, pid, nlmsg_seq++, src_mod, payload_len);
nlh->nlmsg_flags = NLM_F_REQUEST;
wnl = (tAniNlHdr *) nlh;
wnl->radio = radio;
memcpy(&wnl->wmsg, wmsg, wmsg_length);
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: Sending Msg Type [0x%X] to pid[%d]\n",
__func__, be16_to_cpu(wmsg->type), pid);
#ifdef PTT_SOCK_DEBUG_VERBOSE
ptt_sock_dump_buf((const unsigned char *)skb->data, skb->len);
#endif
err = nl_srv_ucast(skb, pid);
nlmsg_failure:
return err;
}
/*
* Process tregisteration request and send registration response messages
* to the PTT Socket App in user space
*/
static void ptt_sock_proc_reg_req(tAniHdr *wmsg, int radio)
{
tAniNlAppRegReq *reg_req;
tAniNlAppRegRsp rspmsg;
reg_req = (tAniNlAppRegReq *)(wmsg + 1);
memset((char *)&rspmsg, 0, sizeof(rspmsg));
//send reg response message to the application
rspmsg.ret = ANI_NL_MSG_OK;
rspmsg.regReq.type = reg_req->type;
/*Save the pid*/
pAdapterHandle->ptt_pid = reg_req->pid;
rspmsg.regReq.pid= reg_req->pid;
rspmsg.wniHdr.type = cpu_to_be16(ANI_MSG_APP_REG_RSP);
rspmsg.wniHdr.length = cpu_to_be16(sizeof(rspmsg));
if (ptt_sock_send_msg_to_app((tAniHdr *)&rspmsg.wniHdr, radio,
ANI_NL_MSG_PUMAC, reg_req->pid) < 0)
{
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Error sending ANI_MSG_APP_REG_RSP to pid[%d]\n",
__func__, reg_req->pid);
}
}
/*
* Process all the messages from the PTT Socket App in user space
*/
static void ptt_proc_pumac_msg(struct sk_buff * skb, tAniHdr *wmsg, int radio)
{
u16 ani_msg_type = be16_to_cpu(wmsg->type);
switch(ani_msg_type)
{
case ANI_MSG_APP_REG_REQ:
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: Received ANI_MSG_APP_REG_REQ [0x%X]\n",
__func__, ani_msg_type);
ptt_sock_proc_reg_req(wmsg, radio);
break;
default:
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Received Unknown Msg Type[0x%X]\n",
__func__, ani_msg_type);
break;
}
}
/*
* Process all the messages from the Quarky Client
*/
static void ptt_proc_quarky_msg(tAniNlHdr *wnl, tAniHdr *wmsg, int radio)
{
u16 ani_msg_type = be16_to_cpu(wmsg->type);
v_U32_t reg_addr;
v_U32_t reg_val;
v_U32_t len_payload;
v_U8_t* buf;
unsigned int arg1, arg2, arg3, arg4, cmd;
VOS_STATUS vosStatus = VOS_STATUS_SUCCESS;
if (radio < 0 || radio > ANI_MAX_RADIOS) {
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: ANI Msg [0x%X] invalid radio id [%d]\n",
__func__, ani_msg_type, radio);
return;
}
if(ani_msg_type == ANI_MSG_APP_REG_REQ)
{
ptt_sock_proc_reg_req(wmsg, radio);
}
else
{
switch (ani_msg_type)
{
case PTT_MSG_READ_REGISTER:
reg_addr = *(v_U32_t*) ((char*)wmsg + 8);
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: PTT_MSG_READ_REGISTER [0x%08lX]\n",
__func__, reg_addr);
vosStatus = sme_DbgReadRegister(pAdapterHandle->hHal, reg_addr, &reg_val);
*(v_U32_t*) ((char*)wmsg + 12) = reg_val;
if(vosStatus != VOS_STATUS_SUCCESS)
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Read Register [0x%08lX] failed!!\n",
__func__, reg_addr);
ptt_sock_send_msg_to_app(wmsg, 0, ANI_NL_MSG_PUMAC, wnl->nlh.nlmsg_pid);
break;
case PTT_MSG_WRITE_REGISTER:
reg_addr = *(v_U32_t*) ((const unsigned char*)wmsg + 8);
reg_val = *(v_U32_t*)((const unsigned char*)wmsg + 12);
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: PTT_MSG_WRITE_REGISTER Addr [0x%08lX] value [0x%08lX]\n",
__func__, reg_addr, reg_val);
vosStatus = sme_DbgWriteRegister(pAdapterHandle->hHal, reg_addr, reg_val);
if(vosStatus != VOS_STATUS_SUCCESS)
{
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Write Register [0x%08lX] value [0x%08lX] failed!!\n",
__func__, reg_addr, reg_val);
}
//send message to the app
ptt_sock_send_msg_to_app(wmsg, 0, ANI_NL_MSG_PUMAC, wnl->nlh.nlmsg_pid);
break;
case PTT_MSG_READ_MEMORY:
reg_addr = *(v_U32_t*) ((char*)wmsg + 8);
len_payload = *(v_U32_t*) ((char*)wmsg + 12);
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: PTT_MSG_READ_MEMORY addr [0x%08lX] bytes [0x%08lX]\n",
__func__, reg_addr, len_payload);
buf = (v_U8_t*)wmsg + 16;
vosStatus = sme_DbgReadMemory(pAdapterHandle->hHal, reg_addr, buf, len_payload);
if(vosStatus != VOS_STATUS_SUCCESS) {
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Memory read failed for [0x%08lX]!!\n",
__func__, reg_addr);
}
ptt_sock_swap_32(buf, len_payload);
//send message to the app
ptt_sock_send_msg_to_app(wmsg, 0, ANI_NL_MSG_PUMAC, wnl->nlh.nlmsg_pid);
break;
case PTT_MSG_WRITE_MEMORY:
reg_addr = *(v_U32_t*) ((char*)wmsg + 8);
len_payload = *(v_U32_t*) ((char*)wmsg + 12);
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: PTT_MSG_DBG_WRITE_MEMORY addr [0x%08lX] bytes [0x%08lX]\n",
__func__, reg_addr, len_payload);
buf = (v_U8_t*)wmsg + 16;
ptt_sock_swap_32(buf, len_payload);
vosStatus = sme_DbgWriteMemory(pAdapterHandle->hHal, reg_addr, buf, len_payload);
if(vosStatus != VOS_STATUS_SUCCESS)
{
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Memory write failed for addr [0x%08lX]!!\n",
__func__, reg_addr);
}
//send message to the app
ptt_sock_send_msg_to_app(wmsg, 0, ANI_NL_MSG_PUMAC, wnl->nlh.nlmsg_pid);
break;
case PTT_MSG_LOG_DUMP_DBG:
cmd = *(unsigned int *) ((char *)wmsg + 8);
arg1 = *(unsigned int *) ((char *)wmsg + 12);
arg2 = *(unsigned int *) ((char *)wmsg + 16);
arg3 = *(unsigned int *) ((char *)wmsg + 20);
arg4 = *(unsigned int *) ((char *)wmsg + 24);
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: PTT_MSG_LOG_DUMP_DBG %d arg1 %d arg2 %d arg3 %d arg4 %d\n",
__func__, cmd, arg1, arg2, arg3, arg4);
#ifdef FEATURE_WLAN_NON_INTEGRATED_SOC
// FIXME_PRIMA -- need logDump() replacement
logPrintf(pAdapterHandle->hHal, cmd, arg1, arg2, arg3, arg4);
#endif //FEATURE_WLAN_NON_INTEGRATED_SOC
//send message to the app
ptt_sock_send_msg_to_app(wmsg, 0, ANI_NL_MSG_PUMAC, wnl->nlh.nlmsg_pid);
break;
#ifdef ANI_MANF_DIAG
case PTT_MSG_FTM_CMDS_TYPE:
wlan_hdd_process_ftm_cmd(pAdapterHandle,wnl);
break;
#endif
default:
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Unknown ANI Msg [0x%X], length [0x%X]\n",
__func__, ani_msg_type, be16_to_cpu(wmsg->length ));
break;
}
}
}
/*
* Process all the Netlink messages from PTT Socket app in user space
*/
static int ptt_sock_rx_nlink_msg (struct sk_buff * skb)
{
tAniNlHdr *wnl;
int radio;
int type;
wnl = (tAniNlHdr *) skb->data;
radio = wnl->radio;
type = wnl->nlh.nlmsg_type;
switch (type) {
case ANI_NL_MSG_PUMAC: //Message from the PTT socket APP
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: Received ANI_NL_MSG_PUMAC Msg [0x%X]\n",
__func__, type, radio);
ptt_proc_pumac_msg(skb, &wnl->wmsg, radio);
break;
case ANI_NL_MSG_PTT: //Message from Quarky GUI
PTT_TRACE(VOS_TRACE_LEVEL_INFO, "%s: Received ANI_NL_MSG_PTT Msg [0x%X]\n",
__func__, type, radio);
ptt_proc_quarky_msg(wnl, &wnl->wmsg, radio);
break;
default:
PTT_TRACE(VOS_TRACE_LEVEL_ERROR, "%s: Unknown NL Msg [0x%X]\n",__func__, type);
break;
}
return 0;
}
int ptt_sock_activate_svc(void *pAdapter)
{
pAdapterHandle = (struct hdd_context_s*)pAdapter;
nl_srv_register(ANI_NL_MSG_PUMAC, ptt_sock_rx_nlink_msg);
nl_srv_register(ANI_NL_MSG_PTT, ptt_sock_rx_nlink_msg);
return 0;
}
#endif //PTT_SOCK_SVC_ENABLE