/* * Copyright (C) 2016 MediaTek Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See http://www.gnu.org/licenses/gpl-2.0.html for more details. */ /* ** Id: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/os/linux/gl_proc.c#1 */ /*! \file "gl_proc.c" \brief This file defines the interface which can interact with users in /proc fs. Detail description. */ /* ** Log: gl_proc.c * * 11 10 2011 cp.wu * [WCXRP00001098] [MT6620 Wi-Fi][Driver] Replace printk by DBG LOG macros in linux porting layer * 1. eliminaite direct calls to printk in porting layer. * 2. replaced by DBGLOG, which would be XLOG on ALPS platforms. * * 12 10 2010 kevin.huang * [WCXRP00000128] [MT6620 Wi-Fi][Driver] Add proc support to Android Driver for debug and driver status check * Add Linux Proc Support ** \main\maintrunk.MT5921\19 2008-09-02 21:08:37 GMT mtk01461 ** Fix the compile error of SPRINTF() ** \main\maintrunk.MT5921\18 2008-08-10 18:48:28 GMT mtk01461 ** Update for Driver Review ** \main\maintrunk.MT5921\17 2008-08-04 16:52:01 GMT mtk01461 ** Add proc dbg print message of DOMAIN_INDEX level ** \main\maintrunk.MT5921\16 2008-07-10 00:45:16 GMT mtk01461 ** Remove the check of MCR offset, we may use the MCR address which is not align to DW boundary or proprietary usage. ** \main\maintrunk.MT5921\15 2008-06-03 20:49:44 GMT mtk01461 ** \main\maintrunk.MT5921\14 2008-06-02 22:56:00 GMT mtk01461 ** Rename some functions for linux proc ** \main\maintrunk.MT5921\13 2008-06-02 20:23:18 GMT mtk01461 ** Revise PROC mcr read / write for supporting TELNET ** \main\maintrunk.MT5921\12 2008-03-28 10:40:25 GMT mtk01461 ** Remove temporary set desired rate in linux proc ** \main\maintrunk.MT5921\11 2008-01-07 15:07:29 GMT mtk01461 ** Add User Update Desired Rate Set for QA in Linux ** \main\maintrunk.MT5921\10 2007-12-11 00:11:14 GMT mtk01461 ** Fix SPIN_LOCK protection ** \main\maintrunk.MT5921\9 2007-12-04 18:07:57 GMT mtk01461 ** Add additional debug category to proc ** \main\maintrunk.MT5921\8 2007-11-02 01:03:23 GMT mtk01461 ** Unify TX Path for Normal and IBSS Power Save + IBSS neighbor learning ** \main\maintrunk.MT5921\7 2007-10-25 18:08:14 GMT mtk01461 ** Add VOIP SCAN Support & Refine Roaming ** Revision 1.3 2007/07/05 07:25:33 MTK01461 ** Add Linux initial code, modify doc, add 11BB, RF init code ** ** Revision 1.2 2007/06/27 02:18:51 MTK01461 ** Update SCAN_FSM, Initial(Can Load Module), Proc(Can do Reg R/W), TX API ** */ /******************************************************************************* * C O M P I L E R F L A G S ******************************************************************************** */ /******************************************************************************* * E X T E R N A L R E F E R E N C E S ******************************************************************************** */ #include "precomp.h" #ifdef FW_CFG_SUPPORT #include "fwcfg.h" #endif /* #include "wlan_lib.h" */ /* #include "debug.h" */ /******************************************************************************* * C O N S T A N T S ******************************************************************************** */ #define PROC_WLAN_THERMO "wlanThermo" #define PROC_DRV_STATUS "status" #define PROC_RX_STATISTICS "rx_statistics" #define PROC_TX_STATISTICS "tx_statistics" #define PROC_DBG_LEVEL_NAME "dbgLevel" #define PROC_NEED_TX_DONE "TxDoneCfg" #define PROC_ROOT_NAME "wlan" #define PROC_CMD_DEBUG_NAME "cmdDebug" #define PROC_CFG_NAME "cfg" #define PROC_MCR_ACCESS_MAX_USER_INPUT_LEN 20 #define PROC_RX_STATISTICS_MAX_USER_INPUT_LEN 10 #define PROC_TX_STATISTICS_MAX_USER_INPUT_LEN 10 #define PROC_DBG_LEVEL_MAX_USER_INPUT_LEN (20*10) #define PROC_DBG_LEVEL_MAX_DISPLAY_STR_LEN 8 #define PROC_UID_SHELL 2000 #define PROC_GID_WIFI 1010 /******************************************************************************* * D A T A T Y P E S ******************************************************************************** */ /******************************************************************************* * P U B L I C D A T A ******************************************************************************** */ /******************************************************************************* * P R I V A T E D A T A ******************************************************************************** */ /* static UINT_32 u4McrOffset; */ #if CFG_SUPPORT_THERMO_THROTTLING static P_GLUE_INFO_T g_prGlueInfo_proc; #endif #if FW_CFG_SUPPORT static P_GLUE_INFO_T gprGlueInfo; #endif /******************************************************************************* * M A C R O S ******************************************************************************** */ /******************************************************************************* * F U N C T I O N D E C L A R A T I O N S ******************************************************************************** */ /******************************************************************************* * F U N C T I O N S ******************************************************************************** */ /*----------------------------------------------------------------------------*/ /*! * \brief The PROC function for reading MCR register to User Space, the offset of * the MCR is specified in u4McrOffset. * * \param[in] page Buffer provided by kernel. * \param[in out] start Start Address to read(3 methods). * \param[in] off Offset. * \param[in] count Allowable number to read. * \param[out] eof End of File indication. * \param[in] data Pointer to the private data structure. * * \return number of characters print to the buffer from User Space. */ /*----------------------------------------------------------------------------*/ #if 0 static int procMCRRead(char *page, char **start, off_t off, int count, int *eof, void *data) { P_GLUE_INFO_T prGlueInfo; PARAM_CUSTOM_MCR_RW_STRUCT_T rMcrInfo; UINT_32 u4BufLen; char *p = page; UINT_32 u4Count; WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS; ASSERT(data); /* Kevin: Apply PROC read method 1. */ if (off != 0) return 0; /* To indicate end of file. */ prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv((struct net_device *)data)); rMcrInfo.u4McrOffset = u4McrOffset; rStatus = kalIoctl(prGlueInfo, wlanoidQueryMcrRead, (PVOID)&rMcrInfo, sizeof(rMcrInfo), TRUE, TRUE, TRUE, FALSE, &u4BufLen); /* SPRINTF(p, ("MCR (0x%08lxh): 0x%08lx\n", */ /* rMcrInfo.u4McrOffset, rMcrInfo.u4McrData)); */ u4Count = (UINT_32) (p - page); *eof = 1; return (int)u4Count; } /* end of procMCRRead() */ /*----------------------------------------------------------------------------*/ /*! * \brief The PROC function for writing MCR register to HW or update u4McrOffset * for reading MCR later. * * \param[in] file pointer to file. * \param[in] buffer Buffer from user space. * \param[in] count Number of characters to write * \param[in] data Pointer to the private data structure. * * \return number of characters write from User Space. */ /*----------------------------------------------------------------------------*/ static int procMCRWrite(struct file *file, const char *buffer, unsigned long count, void *data) { P_GLUE_INFO_T prGlueInfo; char acBuf[PROC_MCR_ACCESS_MAX_USER_INPUT_LEN + 1]; /* + 1 for "\0" */ int i4CopySize; PARAM_CUSTOM_MCR_RW_STRUCT_T rMcrInfo; UINT_32 u4BufLen; WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS; ASSERT(data); i4CopySize = (count < (sizeof(acBuf) - 1)) ? count : (sizeof(acBuf) - 1); if (copy_from_user(acBuf, buffer, i4CopySize)) return 0; acBuf[i4CopySize] = '\0'; if (sscanf(acBuf, "0x%lx 0x%lx", &rMcrInfo.u4McrOffset, &rMcrInfo.u4McrData) == 2) { /* NOTE: Sometimes we want to test if bus will still be ok, after accessing * the MCR which is not align to DW boundary. */ /* if (IS_ALIGN_4(rMcrInfo.u4McrOffset)) */ prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv((struct net_device *)data)); u4McrOffset = rMcrInfo.u4McrOffset; /* printk("Write 0x%lx to MCR 0x%04lx\n", */ /* rMcrInfo.u4McrOffset, rMcrInfo.u4McrData); */ rStatus = kalIoctl(prGlueInfo, wlanoidSetMcrWrite, (PVOID)&rMcrInfo, sizeof(rMcrInfo), FALSE, FALSE, TRUE, FALSE, &u4BufLen); } if (sscanf(acBuf, "0x%lx 0x%lx", &rMcrInfo.u4McrOffset, &rMcrInfo.u4McrData) == 1) { /* if (IS_ALIGN_4(rMcrInfo.u4McrOffset)) */ u4McrOffset = rMcrInfo.u4McrOffset; } return count; } /* end of procMCRWrite() */ #endif #if 0 /*----------------------------------------------------------------------------*/ /*! * \brief The PROC function for reading Driver Status to User Space. * * \param[in] page Buffer provided by kernel. * \param[in out] start Start Address to read(3 methods). * \param[in] off Offset. * \param[in] count Allowable number to read. * \param[out] eof End of File indication. * \param[in] data Pointer to the private data structure. * * \return number of characters print to the buffer from User Space. */ /*----------------------------------------------------------------------------*/ static int procDrvStatusRead(char *page, char **start, off_t off, int count, int *eof, void *data) { P_GLUE_INFO_T prGlueInfo = ((struct net_device *)data)->priv; char *p = page; UINT_32 u4Count; GLUE_SPIN_LOCK_DECLARATION(); ASSERT(data); /* Kevin: Apply PROC read method 1. */ if (off != 0) return 0; /* To indicate end of file. */ SPRINTF(p, ("GLUE LAYER STATUS:")); SPRINTF(p, ("\n==================")); SPRINTF(p, ("\n* Number of Pending Frames: %ld\n", prGlueInfo->u4TxPendingFrameNum)); GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); wlanoidQueryDrvStatusForLinuxProc(prGlueInfo->prAdapter, p, &u4Count); GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); u4Count += (UINT_32) (p - page); *eof = 1; return (int)u4Count; } /* end of procDrvStatusRead() */ /*----------------------------------------------------------------------------*/ /*! * \brief The PROC function for reading Driver RX Statistic Counters to User Space. * * \param[in] page Buffer provided by kernel. * \param[in out] start Start Address to read(3 methods). * \param[in] off Offset. * \param[in] count Allowable number to read. * \param[out] eof End of File indication. * \param[in] data Pointer to the private data structure. * * \return number of characters print to the buffer from User Space. */ /*----------------------------------------------------------------------------*/ static int procRxStatisticsRead(char *page, char **start, off_t off, int count, int *eof, void *data) { P_GLUE_INFO_T prGlueInfo = ((struct net_device *)data)->priv; char *p = page; UINT_32 u4Count; GLUE_SPIN_LOCK_DECLARATION(); ASSERT(data); /* Kevin: Apply PROC read method 1. */ if (off != 0) return 0; /* To indicate end of file. */ SPRINTF(p, ("RX STATISTICS (Write 1 to clear):")); SPRINTF(p, ("\n=================================\n")); GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); wlanoidQueryRxStatisticsForLinuxProc(prGlueInfo->prAdapter, p, &u4Count); GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); u4Count += (UINT_32) (p - page); *eof = 1; return (int)u4Count; } /* end of procRxStatisticsRead() */ /*----------------------------------------------------------------------------*/ /*! * \brief The PROC function for reset Driver RX Statistic Counters. * * \param[in] file pointer to file. * \param[in] buffer Buffer from user space. * \param[in] count Number of characters to write * \param[in] data Pointer to the private data structure. * * \return number of characters write from User Space. */ /*----------------------------------------------------------------------------*/ static int procRxStatisticsWrite(struct file *file, const char *buffer, unsigned long count, void *data) { P_GLUE_INFO_T prGlueInfo = ((struct net_device *)data)->priv; char acBuf[PROC_RX_STATISTICS_MAX_USER_INPUT_LEN + 1]; /* + 1 for "\0" */ UINT_32 u4CopySize; UINT_32 u4ClearCounter; GLUE_SPIN_LOCK_DECLARATION(); ASSERT(data); u4CopySize = (count < (sizeof(acBuf) - 1)) ? count : (sizeof(acBuf) - 1); copy_from_user(acBuf, buffer, u4CopySize); acBuf[u4CopySize] = '\0'; if (kstrtoint(acBuf, 10, &u4ClearCounter) == 1) { if (u4ClearCounter == 1) { GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); wlanoidSetRxStatisticsForLinuxProc(prGlueInfo->prAdapter); GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); } } return count; } /* end of procRxStatisticsWrite() */ /*----------------------------------------------------------------------------*/ /*! * \brief The PROC function for reading Driver TX Statistic Counters to User Space. * * \param[in] page Buffer provided by kernel. * \param[in out] start Start Address to read(3 methods). * \param[in] off Offset. * \param[in] count Allowable number to read. * \param[out] eof End of File indication. * \param[in] data Pointer to the private data structure. * * \return number of characters print to the buffer from User Space. */ /*----------------------------------------------------------------------------*/ static int procTxStatisticsRead(char *page, char **start, off_t off, int count, int *eof, void *data) { P_GLUE_INFO_T prGlueInfo = ((struct net_device *)data)->priv; char *p = page; UINT_32 u4Count; GLUE_SPIN_LOCK_DECLARATION(); ASSERT(data); /* Kevin: Apply PROC read method 1. */ if (off != 0) return 0; /* To indicate end of file. */ SPRINTF(p, ("TX STATISTICS (Write 1 to clear):")); SPRINTF(p, ("\n=================================\n")); GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); wlanoidQueryTxStatisticsForLinuxProc(prGlueInfo->prAdapter, p, &u4Count); GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); u4Count += (UINT_32) (p - page); *eof = 1; return (int)u4Count; } /* end of procTxStatisticsRead() */ /*----------------------------------------------------------------------------*/ /*! * \brief The PROC function for reset Driver TX Statistic Counters. * * \param[in] file pointer to file. * \param[in] buffer Buffer from user space. * \param[in] count Number of characters to write * \param[in] data Pointer to the private data structure. * * \return number of characters write from User Space. */ /*----------------------------------------------------------------------------*/ static int procTxStatisticsWrite(struct file *file, const char *buffer, unsigned long count, void *data) { P_GLUE_INFO_T prGlueInfo = ((struct net_device *)data)->priv; char acBuf[PROC_RX_STATISTICS_MAX_USER_INPUT_LEN + 1]; /* + 1 for "\0" */ UINT_32 u4CopySize; UINT_32 u4ClearCounter; GLUE_SPIN_LOCK_DECLARATION(); ASSERT(data); u4CopySize = (count < (sizeof(acBuf) - 1)) ? count : (sizeof(acBuf) - 1); copy_from_user(acBuf, buffer, u4CopySize); acBuf[u4CopySize] = '\0'; if (kstrtoint(acBuf, 10, &u4ClearCounter) == 1) { if (u4ClearCounter == 1) { GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); wlanoidSetTxStatisticsForLinuxProc(prGlueInfo->prAdapter); GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_FSM); } } return count; } /* end of procTxStatisticsWrite() */ #endif static struct proc_dir_entry *gprProcRoot; static UINT_8 aucDbModuleName[][PROC_DBG_LEVEL_MAX_DISPLAY_STR_LEN] = { "INIT", "HAL", "INTR", "REQ", "TX", "RX", "RFTEST", "EMU", "SW1", "SW2", "SW3", "SW4", "HEM", "AIS", "RLM", "MEM", "CNM", "RSN", "BSS", "SCN", "SAA", "AAA", "P2P", "QM", "SEC", "BOW", "WAPI", "ROAMING", "TDLS", "OID", "NIC" }; static UINT_8 aucProcBuf[1536]; static ssize_t procDbgLevelRead(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { UINT_8 *temp = &aucProcBuf[0]; UINT_32 u4CopySize = 0; UINT_16 i; UINT_16 u2ModuleNum = 0; /* if *f_ops>0, we should return 0 to make cat command exit */ if (*f_pos > 0) return 0; kalStrCpy(temp, "\nTEMP |LOUD |INFO |TRACE|EVENT|STATE|WARN |ERROR\n" "bit7 |bit6 |bit5 |bit4 |bit3 |bit2 |bit1 |bit0\n\n" "Debug Module\tIndex\tLevel\tDebug Module\tIndex\tLevel\n\n"); temp += kalStrLen(temp); u2ModuleNum = (sizeof(aucDbModuleName) / PROC_DBG_LEVEL_MAX_DISPLAY_STR_LEN) & 0xfe; for (i = 0; i < u2ModuleNum; i += 2) SPRINTF(temp, ("DBG_%s_IDX\t(0x%02x):\t0x%02x\tDBG_%s_IDX\t(0x%02x):\t0x%02x\n", &aucDbModuleName[i][0], i, aucDebugModule[i], &aucDbModuleName[i+1][0], i+1, aucDebugModule[i+1])); if ((sizeof(aucDbModuleName) / PROC_DBG_LEVEL_MAX_DISPLAY_STR_LEN) & 0x1) SPRINTF(temp, ("DBG_%s_IDX\t(0x%02x):\t0x%02x\n", &aucDbModuleName[u2ModuleNum][0], u2ModuleNum, aucDebugModule[u2ModuleNum])); u4CopySize = kalStrLen(aucProcBuf); if (u4CopySize > count) u4CopySize = count; if (copy_to_user(buf, aucProcBuf, u4CopySize)) { kalPrint("copy to user failed\n"); return -EFAULT; } *f_pos += u4CopySize; return (ssize_t)u4CopySize; } static ssize_t procDbgLevelWrite(struct file *file, const char *buffer, size_t count, loff_t *data) { UINT_32 u4NewDbgModule, u4NewDbgLevel; UINT_8 i = 0; UINT_32 u4CopySize = sizeof(aucProcBuf); UINT_8 *temp = &aucProcBuf[0]; kalMemSet(aucProcBuf, 0, u4CopySize); u4CopySize = (count < u4CopySize) ? count : (u4CopySize - 1); if (copy_from_user(aucProcBuf, buffer, u4CopySize)) { kalPrint("error of copy from user\n"); return -EFAULT; } aucProcBuf[u4CopySize] = '\0'; while (temp) { if (sscanf(temp, "0x%x:0x%x", &u4NewDbgModule, &u4NewDbgLevel) != 2) { kalPrint("debug module and debug level should be one byte in length\n"); break; } if (u4NewDbgModule == 0xFF) { for (i = 0; i < DBG_MODULE_NUM; i++) aucDebugModule[i] = u4NewDbgLevel & DBG_CLASS_MASK; break; } else if (u4NewDbgModule >= DBG_MODULE_NUM) { kalPrint("debug module index should less than %d\n", DBG_MODULE_NUM); break; } aucDebugModule[u4NewDbgModule] = u4NewDbgLevel & DBG_CLASS_MASK; temp = kalStrChr(temp, ','); if (!temp) break; temp++; /* skip ',' */ } return count; } static const struct file_operations dbglevel_ops = { .owner = THIS_MODULE, .read = procDbgLevelRead, .write = procDbgLevelWrite, }; static ssize_t procTxDoneCfgRead(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { UINT_8 *temp = &aucProcBuf[0]; UINT_32 u4CopySize = 0; UINT_16 u2TxDoneCfg = 0; /* if *f_ops>0, we should return 0 to make cat command exit */ if (*f_pos > 0) return 0; u2TxDoneCfg = StatsGetCfgTxDone(); SPRINTF(temp, ("Tx Done Configure:\nARP %d\tDNS %d\nTCP %d\tUDP %d\nEAPOL %d\tDHCP %d\nICMP %d\n", !!(u2TxDoneCfg & CFG_ARP), !!(u2TxDoneCfg & CFG_DNS), !!(u2TxDoneCfg & CFG_TCP), !!(u2TxDoneCfg & CFG_UDP), !!(u2TxDoneCfg & CFG_EAPOL), !!(u2TxDoneCfg & CFG_DHCP), !!(u2TxDoneCfg & CFG_ICMP))); u4CopySize = kalStrLen(aucProcBuf); if (u4CopySize > count) u4CopySize = count; if (copy_to_user(buf, aucProcBuf, u4CopySize)) { kalPrint("copy to user failed\n"); return -EFAULT; } *f_pos += u4CopySize; return (ssize_t)u4CopySize; } static ssize_t procTxDoneCfgWrite(struct file *file, const char *buffer, size_t count, loff_t *data) { #define MODULE_NAME_LENGTH 6 UINT_8 i = 0; UINT_32 u4CopySize = sizeof(aucProcBuf); UINT_8 *temp = &aucProcBuf[0]; UINT_16 u2SetTxDoneCfg = 0; UINT_16 u2ClsTxDoneCfg = 0; UINT_8 aucModule[MODULE_NAME_LENGTH]; UINT_32 u4Enabled; UINT_8 aucModuleArray[][MODULE_NAME_LENGTH] = {"ARP", "DNS", "TCP", "UDP", "EAPOL", "DHCP", "ICMP"}; kalMemSet(aucProcBuf, 0, u4CopySize); u4CopySize = (count < u4CopySize) ? count : (u4CopySize - 1); if (copy_from_user(aucProcBuf, buffer, u4CopySize)) { kalPrint("error of copy from user\n"); return -EFAULT; } aucProcBuf[u4CopySize] = '\0'; temp = &aucProcBuf[0]; while (temp) { /* pick up a string and teminated after meet : */ if (sscanf(temp, "%5s %d", aucModule, &u4Enabled) != 2) { kalPrint("read param fail, aucModule=%s\n", aucModule); break; } for (i = 0; i < sizeof(aucModuleArray)/MODULE_NAME_LENGTH; i++) { if (kalStrniCmp(aucModule, aucModuleArray[i], MODULE_NAME_LENGTH) == 0) { if (u4Enabled) u2SetTxDoneCfg |= 1 << i; else u2ClsTxDoneCfg |= 1 << i; break; } } temp = kalStrChr(temp, ','); if (!temp) break; temp++; /* skip ',' */ } if (u2SetTxDoneCfg) StatsSetCfgTxDone(u2SetTxDoneCfg, TRUE); if (u2ClsTxDoneCfg) StatsSetCfgTxDone(u2ClsTxDoneCfg, FALSE); return count; } static const struct file_operations proc_txdone_ops = { .owner = THIS_MODULE, .read = procTxDoneCfgRead, .write = procTxDoneCfgWrite, }; static ssize_t procCmdDebug(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { UINT_32 u4CopySize = 0; /* if *f_ops>0, we should return 0 to make cat command exit */ if (*f_pos > 0) return 0; wlanDumpTcResAndTxedCmd(aucProcBuf, sizeof(aucProcBuf)); u4CopySize = kalStrLen(aucProcBuf); if (u4CopySize > count) u4CopySize = count; if (copy_to_user(buf, aucProcBuf, u4CopySize)) { kalPrint("copy to user failed\n"); return -EFAULT; } *f_pos += u4CopySize; return (ssize_t)u4CopySize; } static const struct file_operations proc_CmdDebug_ops = { .owner = THIS_MODULE, .read = procCmdDebug, }; /*----------------------------------------------------------------------------*/ /*! * \brief This function create a PROC fs in linux /proc/net subdirectory. * * \param[in] prDev Pointer to the struct net_device. * \param[in] pucDevName Pointer to the name of net_device. * * \return N/A */ /*----------------------------------------------------------------------------*/ #if CFG_SUPPORT_THERMO_THROTTLING /** * This function is called then the /proc file is read * */ typedef struct _COEX_BUF1 { UINT8 buffer[128]; INT32 availSize; } COEX_BUF1, *P_COEX_BUF1; COEX_BUF1 gCoexBuf1; static ssize_t procfile_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { INT32 retval = 0; INT32 i_ret = 0; CHAR *warn_msg = "no data available, please run echo 15 xx > /proc/driver/wmt_psm first\n"; if (*f_pos > 0) { retval = 0; } else { /*len = sprintf(page, "%d\n", g_psm_enable); */ #if 1 if (gCoexBuf1.availSize <= 0) { DBGLOG(INIT, WARN, "no data available\n"); retval = strlen(warn_msg) + 1; if (count < retval) retval = count; i_ret = copy_to_user(buf, warn_msg, retval); if (i_ret) { DBGLOG(INIT, ERROR, "copy to buffer failed, ret:%d\n", retval); retval = -EFAULT; goto err_exit; } *f_pos += retval; } else #endif { INT32 i = 0; INT32 len = 0; CHAR msg_info[128]; INT32 max_num = 0; /*we do not check page buffer, because there are only 100 bytes in g_coex_buf, no reason page buffer is not enough, a bomb is placed here on unexpected condition */ DBGLOG(INIT, TRACE, "%d bytes available\n", gCoexBuf1.availSize); max_num = ((sizeof(msg_info) > count ? sizeof(msg_info) : count) - 1) / 5; if (max_num > gCoexBuf1.availSize) max_num = gCoexBuf1.availSize; else DBGLOG(INIT, TRACE, "round to %d bytes due to local buffer size limitation\n", max_num); for (i = 0; i < max_num; i++) len += sprintf(msg_info + len, "%d", gCoexBuf1.buffer[i]); len += sprintf(msg_info + len, "\n"); retval = len; i_ret = copy_to_user(buf, msg_info, retval); if (i_ret) { DBGLOG(INIT, ERROR, "copy to buffer failed, ret:%d\n", retval); retval = -EFAULT; goto err_exit; } *f_pos += retval; } } gCoexBuf1.availSize = 0; err_exit: return retval; } #if 1 typedef INT32 (*WLAN_DEV_DBG_FUNC)(void); static INT32 wlan_get_thermo_power(void); static INT32 wlan_get_link_mode(void); static const WLAN_DEV_DBG_FUNC wlan_dev_dbg_func[] = { [0] = wlan_get_thermo_power, [1] = wlan_get_link_mode, }; INT32 wlan_get_thermo_power(void) { P_ADAPTER_T prAdapter; prAdapter = g_prGlueInfo_proc->prAdapter; if (prAdapter->u4AirDelayTotal > 100) gCoexBuf1.buffer[0] = 100; else gCoexBuf1.buffer[0] = prAdapter->u4AirDelayTotal; gCoexBuf1.availSize = 1; DBGLOG(RLM, TRACE, "PROC %s thrmo_power(%d)\n", __func__, gCoexBuf1.buffer[0]); return 0; } INT32 wlan_get_link_mode(void) { UINT_8 ucLinkMode = 0; P_ADAPTER_T prAdapter; BOOLEAN fgIsAPmode; prAdapter = g_prGlueInfo_proc->prAdapter; fgIsAPmode = p2pFuncIsAPMode(prAdapter->rWifiVar.prP2pFsmInfo); DBGLOG(RLM, TRACE, "PROC %s AIS(%d)P2P(%d)AP(%d)\n", __func__, prAdapter->rWifiVar.arBssInfo[NETWORK_TYPE_AIS_INDEX].eConnectionState, prAdapter->rWifiVar.arBssInfo[NETWORK_TYPE_P2P_INDEX].eConnectionState, fgIsAPmode); #if 1 if (prAdapter->rWifiVar.arBssInfo[NETWORK_TYPE_AIS_INDEX].eConnectionState == PARAM_MEDIA_STATE_CONNECTED) ucLinkMode |= BIT(0); if (prAdapter->rWifiVar.arBssInfo[NETWORK_TYPE_P2P_INDEX].eConnectionState == PARAM_MEDIA_STATE_CONNECTED) ucLinkMode |= BIT(1); if (fgIsAPmode) ucLinkMode |= BIT(2); #endif gCoexBuf1.buffer[0] = ucLinkMode; gCoexBuf1.availSize = 1; return 0; } static ssize_t procfile_write(struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos) { char buf[256]; char *pBuf; ULONG len = count; unsigned int x = 0; char *pToken = NULL; char *pDelimiter = " \t"; INT32 i4Ret = -1; DBGLOG(INIT, TRACE, "write parameter len = %d\n\r", (INT32) len); if (len >= sizeof(buf)) { DBGLOG(INIT, ERROR, "input handling fail!\n"); return -1; } if (copy_from_user(buf, buffer, len)) return -EFAULT; buf[len] = '\0'; DBGLOG(INIT, TRACE, "write parameter data = %s\n\r", buf); pBuf = buf; pToken = strsep(&pBuf, pDelimiter); if (pToken) { i4Ret = kalkStrtou32(pToken, 16, &x); if (!i4Ret) DBGLOG(INIT, TRACE, " x(0x%08x)\n\r", x); } if ((!i4Ret) && ((sizeof(wlan_dev_dbg_func) / sizeof(wlan_dev_dbg_func[0])) > x) && (wlan_dev_dbg_func[x] != NULL)) (*wlan_dev_dbg_func[x]) (); else DBGLOG(INIT, ERROR, "no handler defined for command id(0x%08x), pToken=%p, i4Ret=%d\n\r", x, pToken, i4Ret); return len; } #endif static const struct file_operations proc_fops = { .owner = THIS_MODULE, .read = procfile_read, .write = procfile_write, }; #endif INT_32 procInitFs(VOID) { struct proc_dir_entry *prEntry; if (init_net.proc_net == (struct proc_dir_entry *)NULL) { kalPrint("init proc fs fail: proc_net == NULL\n"); return -ENOENT; } /* * Directory: Root (/proc/net/wlan0) */ gprProcRoot = proc_mkdir(PROC_ROOT_NAME, init_net.proc_net); if (!gprProcRoot) { kalPrint("gprProcRoot == NULL\n"); return -ENOENT; } proc_set_user(gprProcRoot, KUIDT_INIT(PROC_UID_SHELL), KGIDT_INIT(PROC_GID_WIFI)); prEntry = proc_create(PROC_DBG_LEVEL_NAME, 0664, gprProcRoot, &dbglevel_ops); if (prEntry == NULL) { kalPrint("Unable to create /proc entry dbgLevel\n\r"); return -1; } proc_set_user(prEntry, KUIDT_INIT(PROC_UID_SHELL), KGIDT_INIT(PROC_GID_WIFI)); prEntry = proc_create(PROC_NEED_TX_DONE, 0664, gprProcRoot, &proc_txdone_ops); if (prEntry == NULL) { kalPrint("Unable to create /proc entry dbgLevel\n\r"); return -1; } proc_set_user(prEntry, KUIDT_INIT(PROC_UID_SHELL), KGIDT_INIT(PROC_GID_WIFI)); return 0; } /* end of procInitProcfs() */ INT_32 procUninitProcFs(VOID) { remove_proc_entry(PROC_DBG_LEVEL_NAME, gprProcRoot); remove_proc_subtree(PROC_ROOT_NAME, init_net.proc_net); return 0; } /*----------------------------------------------------------------------------*/ /*! * \brief This function clean up a PROC fs created by procInitProcfs(). * * \param[in] prDev Pointer to the struct net_device. * \param[in] pucDevName Pointer to the name of net_device. * * \return N/A */ /*----------------------------------------------------------------------------*/ INT_32 procRemoveProcfs(VOID) { /* remove root directory (proc/net/wlan0) */ /* remove_proc_entry(pucDevName, init_net.proc_net); */ remove_proc_entry(PROC_WLAN_THERMO, gprProcRoot); remove_proc_entry(PROC_CMD_DEBUG_NAME, gprProcRoot); #if CFG_SUPPORT_THERMO_THROTTLING g_prGlueInfo_proc = NULL; #endif return 0; } /* end of procRemoveProcfs() */ INT_32 procCreateFsEntry(P_GLUE_INFO_T prGlueInfo) { struct proc_dir_entry *prEntry; DBGLOG(INIT, TRACE, "[%s]\n", __func__); #if CFG_SUPPORT_THERMO_THROTTLING g_prGlueInfo_proc = prGlueInfo; #endif prGlueInfo->pProcRoot = gprProcRoot; prEntry = proc_create(PROC_WLAN_THERMO, 0664, gprProcRoot, &proc_fops); if (prEntry == NULL) { DBGLOG(INIT, ERROR, "Unable to create /proc entry\n\r"); return -1; } prEntry = proc_create(PROC_CMD_DEBUG_NAME, 0444, gprProcRoot, &proc_CmdDebug_ops); if (prEntry == NULL) { kalPrint("Unable to create /proc entry dbgLevel\n\r"); return -1; } proc_set_user(prEntry, KUIDT_INIT(PROC_UID_SHELL), KGIDT_INIT(PROC_GID_WIFI)); return 0; } #ifdef FW_CFG_SUPPORT #define MAX_CFG_OUTPUT_BUF_LENGTH 1024 static UINT_8 aucCfgBuf[CMD_FORMAT_V1_LENGTH]; static UINT_8 aucCfgQueryKey[MAX_CMD_NAME_MAX_LENGTH]; static UINT_8 aucCfgOutputBuf[MAX_CFG_OUTPUT_BUF_LENGTH]; static ssize_t cfgRead(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { WLAN_STATUS rStatus = WLAN_STATUS_FAILURE; UINT_8 *temp = &aucCfgOutputBuf[0]; UINT_32 u4CopySize = 0; struct _CMD_HEADER_T cmdV1Header; struct _CMD_FORMAT_V1_T *pr_cmd_v1 = (struct _CMD_FORMAT_V1_T *) cmdV1Header.buffer; /* if *f_pos > 0, we should return 0 to make cat command exit */ if (*f_pos > 0 || gprGlueInfo == NULL) return 0; kalMemSet(aucCfgOutputBuf, 0, MAX_CFG_OUTPUT_BUF_LENGTH); SPRINTF(temp, ("\nprocCfgRead() %s:\n", aucCfgQueryKey)); /* send to FW */ cmdV1Header.cmdVersion = CMD_VER_1; cmdV1Header.cmdType = CMD_TYPE_QUERY; cmdV1Header.itemNum = 1; cmdV1Header.cmdBufferLen = sizeof(struct _CMD_FORMAT_V1_T); kalMemSet(cmdV1Header.buffer, 0, MAX_CMD_BUFFER_LENGTH); pr_cmd_v1->itemStringLength = kalStrLen(aucCfgQueryKey); kalMemCopy(pr_cmd_v1->itemString, aucCfgQueryKey, kalStrLen(aucCfgQueryKey)); rStatus = kalIoctl(gprGlueInfo, wlanoidQueryCfgRead, (PVOID)&cmdV1Header, sizeof(cmdV1Header), TRUE, TRUE, TRUE, FALSE, &u4CopySize); if (rStatus == WLAN_STATUS_FAILURE) DBGLOG(INIT, ERROR, "prCmdV1Header kalIoctl wlanoidQueryCfgRead fail 0x%x\n", rStatus); SPRINTF(temp, ("%s\n", cmdV1Header.buffer)); u4CopySize = kalStrLen(aucCfgOutputBuf); if (u4CopySize > count) u4CopySize = count; if (copy_to_user(buf, aucCfgOutputBuf, u4CopySize)) DBGLOG(INIT, ERROR, "copy to user failed\n"); *f_pos += u4CopySize; return (ssize_t)u4CopySize; } static ssize_t cfgWrite(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { /* echo xxx xxx > /proc/net/wlan/cfg */ UINT_8 i = 0; UINT_32 u4CopySize = sizeof(aucCfgBuf); UINT_8 token_num = 1; kalMemSet(aucCfgBuf, 0, u4CopySize); u4CopySize = (count < u4CopySize) ? count : (u4CopySize - 1); if (copy_from_user(aucCfgBuf, buf, u4CopySize)) { DBGLOG(INIT, ERROR, "copy from user failed\n"); return -EFAULT; } for (; i < u4CopySize; i++) { if (aucCfgBuf[i] == ' ') { token_num++; break; } } DBGLOG(INIT, INFO, "procCfgWrite %s\n", aucCfgBuf); if (token_num == 1) { kalMemSet(aucCfgQueryKey, 0, sizeof(aucCfgQueryKey)); memcpy(aucCfgQueryKey, aucCfgBuf, u4CopySize); if (aucCfgQueryKey[u4CopySize - 1] == 0x0a) aucCfgQueryKey[u4CopySize - 1] = '\0'; } else { if (u4CopySize) wlanFwCfgParse(gprGlueInfo->prAdapter, aucCfgBuf); } return count; } static const struct file_operations cfg_ops = { .owner = THIS_MODULE, .read = cfgRead, .write = cfgWrite, }; INT_32 cfgRemoveProcEntry(void) { remove_proc_entry(PROC_CFG_NAME, gprProcRoot); return 0; } INT_32 cfgCreateProcEntry(P_GLUE_INFO_T prGlueInfo) { struct proc_dir_entry *prEntry; prGlueInfo->pProcRoot = gprProcRoot; gprGlueInfo = prGlueInfo; prEntry = proc_create(PROC_CFG_NAME, 0664, gprProcRoot, &cfg_ops); if (prEntry == NULL) { DBGLOG(INIT, ERROR, "Unable to create /proc entry cfg\n\r"); return -1; } proc_set_user(prEntry, KUIDT_INIT(PROC_UID_SHELL), KGIDT_INIT(PROC_GID_WIFI)); return 0; } #endif