| /*! \file |
| \brief brief description |
| |
| Detailed descriptions here. |
| |
| */ |
| |
| |
| |
| /******************************************************************************* |
| * C O M P I L E R F L A G S |
| ******************************************************************************** |
| */ |
| |
| /******************************************************************************* |
| * M A C R O S |
| ******************************************************************************** |
| */ |
| |
| |
| |
| /******************************************************************************* |
| * E X T E R N A L R E F E R E N C E S |
| ******************************************************************************** |
| */ |
| |
| #include "osal_typedef.h" |
| #include "osal.h" |
| #include "wmt_dev.h" |
| #include "wmt_core.h" |
| #include "wmt_exp.h" |
| #include "wmt_lib.h" |
| #include "wmt_conf.h" |
| #include "psm_core.h" |
| #include "stp_core.h" |
| #include "stp_exp.h" |
| #include "hif_sdio.h" |
| #include "wmt_dbg.h" |
| #include "wmt_idc.h" |
| #include <linux/device.h> |
| |
| |
| #define MTK_WMT_VERSION "Combo WMT Driver - v1.0" |
| #define MTK_WMT_DATE "2011/10/04" |
| #define MTK_COMBO_DRIVER_VERSION "APEX.WCN.MT6620.JB2.MP.V1.0" |
| #define WMT_DEV_MAJOR 190 /* never used number */ |
| #define WMT_DEV_NUM 1 |
| #define WMT_DEV_INIT_TO_MS (2 * 1000) |
| #define WMT_MAX_PATCH_NUM (0x200) |
| |
| #if CFG_WMT_PROC_FOR_AEE |
| static struct proc_dir_entry *gWmtAeeEntry; |
| #define WMT_AEE_PROCNAME "driver/wmt_aee" |
| #define WMT_PROC_AEE_SIZE 3072 |
| static UINT32 g_buf_len; |
| static UINT8 *pBuf; |
| |
| |
| |
| #if USE_NEW_PROC_FS_FLAG |
| ssize_t wmt_aee_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos); |
| ssize_t wmt_aee_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); |
| static struct file_operations wmt_aee_fops = { |
| .read = wmt_aee_read, |
| .write = wmt_aee_write, |
| }; |
| #endif |
| |
| |
| #endif |
| |
| |
| #define WMT_DRIVER_NAME "mtk_stp_wmt" |
| |
| |
| P_OSAL_EVENT gpRxEvent = NULL; |
| |
| UINT32 u4RxFlag = 0x0; |
| static atomic_t gRxCount = ATOMIC_INIT(0); |
| |
| /* Linux UCHAR device */ |
| static int gWmtMajor = WMT_DEV_MAJOR; |
| static struct cdev gWmtCdev; |
| static atomic_t gWmtRefCnt = ATOMIC_INIT(0); |
| /* WMT driver information */ |
| static UINT8 gLpbkBuf[WMT_LPBK_BUF_LEN] = { 0 }; /* modify for support 1024 loopback */ |
| |
| static UINT32 gLpbkBufLog; /* George LPBK debug */ |
| static INT32 gWmtInitDone; |
| static wait_queue_head_t gWmtInitWq; |
| |
| P_WMT_PATCH_INFO pPatchInfo = NULL; |
| UINT32 pAtchNum = 0; |
| |
| |
| /******************************************************************************* |
| * F U N C T I O N S |
| ******************************************************************************** |
| */ |
| |
| #if CFG_WMT_PROC_FOR_AEE |
| |
| #if USE_NEW_PROC_FS_FLAG |
| ssize_t wmt_aee_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) |
| { |
| INT32 retval = 0; |
| UINT32 len = 0; |
| WMT_INFO_FUNC("%s: count %d pos %lld\n", __func__, count, *f_pos); |
| |
| if (0 == *f_pos) { |
| pBuf = wmt_lib_get_cpupcr_xml_format(&len); |
| g_buf_len = len; |
| WMT_INFO_FUNC("wmt_dev:wmt for aee buffer len(%d)\n", g_buf_len); |
| } |
| |
| if (g_buf_len >= count) { |
| |
| retval = copy_to_user(buf, pBuf, count); |
| if (retval) { |
| WMT_ERR_FUNC("copy to aee buffer failed, ret:%d\n", retval); |
| retval = -EFAULT; |
| goto err_exit; |
| } |
| |
| *f_pos += count; |
| g_buf_len -= count; |
| pBuf += count; |
| WMT_INFO_FUNC("wmt_dev:after read,wmt for aee buffer len(%d)\n", g_buf_len); |
| |
| retval = count; |
| } else if (0 != g_buf_len) { |
| |
| retval = copy_to_user(buf, pBuf, g_buf_len); |
| if (retval) { |
| WMT_ERR_FUNC("copy to aee buffer failed, ret:%d\n", retval); |
| retval = -EFAULT; |
| goto err_exit; |
| } |
| |
| *f_pos += g_buf_len; |
| len = g_buf_len; |
| g_buf_len = 0; |
| pBuf += len; |
| retval = len; |
| WMT_INFO_FUNC("wmt_dev:after read,wmt for aee buffer len(%d)\n", g_buf_len); |
| } else { |
| WMT_INFO_FUNC("wmt_dev: no data avaliable for aee\n"); |
| retval = 0; |
| } |
| err_exit: |
| return retval; |
| } |
| |
| |
| ssize_t wmt_aee_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) |
| { |
| WMT_TRC_FUNC(); |
| return 0; |
| } |
| |
| #else |
| static UINT32 passCnt; |
| static int wmt_dev_proc_for_aee_read(char *page, char **start, off_t off, int count, int *eof, |
| void *data) |
| { |
| UINT32 len = 0; |
| |
| WMT_INFO_FUNC("wmt-dev:wmt for aee page(%p)off(%d)count(%d)\n", page, off, count); |
| |
| if (off == 0) { |
| pBuf = wmt_lib_get_cpupcr_xml_format(&len); |
| g_buf_len = len; |
| |
| /*pass 3k buffer for each proc read */ |
| passCnt = g_buf_len / WMT_PROC_AEE_SIZE; |
| passCnt = (g_buf_len % WMT_PROC_AEE_SIZE) ? (passCnt + 1) : passCnt; |
| WMT_INFO_FUNC("wmt_dev:wmt for aee buffer len(%d)passCnt(%d)\n", g_buf_len, |
| passCnt); |
| } |
| |
| if (passCnt) { |
| if (g_buf_len > WMT_PROC_AEE_SIZE) { |
| osal_memcpy(page, pBuf, WMT_PROC_AEE_SIZE); |
| *start += WMT_PROC_AEE_SIZE; |
| g_buf_len -= WMT_PROC_AEE_SIZE; |
| pBuf += WMT_PROC_AEE_SIZE; |
| WMT_INFO_FUNC("wmt_dev:after read,wmt for aee buffer len(%d)\n", g_buf_len); |
| *eof = 1; |
| passCnt--; |
| return WMT_PROC_AEE_SIZE; |
| } else { |
| osal_memcpy(page, pBuf, g_buf_len); |
| *start += g_buf_len; |
| len = g_buf_len; |
| g_buf_len = 0; |
| *eof = 1; |
| passCnt--; |
| pBuf += len; |
| return len; |
| } |
| } |
| |
| return len; |
| } |
| |
| |
| static int wmt_dev_proc_for_aee_write(struct file *file, const char *buffer, unsigned long count, |
| void *data) |
| { |
| return 0; |
| } |
| |
| #endif |
| |
| INT32 wmt_dev_proc_for_aee_setup(VOID) |
| { |
| INT32 i_ret = 0; |
| #if USE_NEW_PROC_FS_FLAG |
| gWmtAeeEntry = proc_create(WMT_AEE_PROCNAME, 0664, NULL, &wmt_aee_fops); |
| if (gWmtAeeEntry == NULL) { |
| WMT_ERR_FUNC("Unable to create / wmt_aee proc entry\n\r"); |
| i_ret = -1; |
| } |
| #else |
| |
| gWmtAeeEntry = create_proc_entry(WMT_AEE_PROCNAME, 0664, NULL); |
| if (gWmtAeeEntry == NULL) { |
| WMT_ERR_FUNC("Unable to create / wmt_aee proc entry\n\r"); |
| i_ret = -1; |
| } |
| gWmtAeeEntry->read_proc = wmt_dev_proc_for_aee_read; |
| gWmtAeeEntry->write_proc = wmt_dev_proc_for_aee_write; |
| #endif |
| return i_ret; |
| } |
| |
| |
| INT32 wmt_dev_proc_for_aee_remove(VOID) |
| { |
| #if USE_NEW_PROC_FS_FLAG |
| if (NULL != gWmtAeeEntry) { |
| proc_remove(gWmtAeeEntry); |
| } |
| #else |
| |
| if (NULL != gWmtAeeEntry) { |
| remove_proc_entry(WMT_AEE_PROCNAME, NULL); |
| } |
| #endif |
| return 0; |
| } |
| #endif /* end of "CFG_WMT_PROC_FOR_AEE" */ |
| |
| |
| VOID wmt_dev_rx_event_cb(VOID) |
| { |
| if (NULL != gpRxEvent) { |
| u4RxFlag = 1; |
| atomic_inc(&gRxCount); |
| wake_up_interruptible(&gpRxEvent->waitQueue); |
| } else { |
| WMT_ERR_FUNC("null gpRxEvent, flush rx!\n"); |
| wmt_lib_flush_rx(); |
| } |
| } |
| |
| |
| INT32 wmt_dev_rx_timeout(P_OSAL_EVENT pEvent) |
| { |
| |
| UINT32 ms = pEvent->timeoutValue; |
| LONG lRet = 0; |
| gpRxEvent = pEvent; |
| if (0 != ms) { |
| lRet = |
| wait_event_interruptible_timeout(gpRxEvent->waitQueue, 0 != u4RxFlag, |
| msecs_to_jiffies(ms)); |
| } else { |
| lRet = wait_event_interruptible(gpRxEvent->waitQueue, u4RxFlag != 0); |
| } |
| u4RxFlag = 0; |
| /* gpRxEvent = NULL; */ |
| if (atomic_dec_return(&gRxCount)) { |
| WMT_ERR_FUNC("gRxCount != 0 (%d), reset it!\n", atomic_read(&gRxCount)); |
| atomic_set(&gRxCount, 0); |
| } |
| |
| return lRet; |
| } |
| |
| INT32 wmt_dev_read_file(UCHAR *pName, const u8 **ppBufPtr, INT32 offset, INT32 padSzBuf) |
| { |
| INT32 iRet = -1; |
| INT32 Ret = 0; |
| INT32 file_len; |
| void *pBuf; |
| const struct firmware *fw = NULL; |
| |
| if (!ppBufPtr) { |
| WMT_ERR_FUNC("invalid ppBufptr!\n"); |
| return -1; |
| } |
| *ppBufPtr = NULL; |
| Ret = request_firmware(&fw, pName, NULL); |
| if (Ret != 0) { |
| WMT_ERR_FUNC("failed to open or read!(%s), %d\n", pName, Ret); |
| return MTK_WCN_BOOL_FALSE; |
| } |
| |
| file_len = fw->size; |
| pBuf = vmalloc((file_len + padSzBuf + 3) & ~0x3UL); |
| if (!pBuf) { |
| WMT_ERR_FUNC("failed to vmalloc(%d)\n", (INT32) ((file_len + 3) & ~0x3UL)); |
| goto read_file_done; |
| } |
| memcpy (pBuf + padSzBuf, fw->data, file_len); |
| iRet = 0; |
| *ppBufPtr = pBuf; |
| read_file_done: |
| if (iRet) { |
| if (pBuf) { |
| vfree(pBuf); |
| } |
| } |
| release_firmware(fw); |
| return (iRet) ? iRet : file_len; |
| } |
| |
| /* TODO: [ChangeFeature][George] refine this function name for general filesystem read operation, not patch only. */ |
| INT32 wmt_dev_patch_get(UCHAR *pPatchName, osal_firmware * *ppPatch, INT32 padSzBuf) |
| { |
| INT32 iRet = -1; |
| osal_firmware *pfw; |
| uid_t orig_uid; |
| gid_t orig_gid; |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) |
| /* struct cred *cred = get_task_cred(current); */ |
| struct cred *cred = (struct cred *)get_current_cred(); |
| #endif |
| |
| mm_segment_t orig_fs = get_fs(); |
| |
| if (*ppPatch) { |
| WMT_WARN_FUNC("f/w patch already exists\n"); |
| if ((*ppPatch)->data) { |
| vfree((*ppPatch)->data); |
| } |
| kfree(*ppPatch); |
| *ppPatch = NULL; |
| } |
| |
| if (!osal_strlen(pPatchName)) { |
| WMT_ERR_FUNC("empty f/w name\n"); |
| osal_assert((osal_strlen(pPatchName) > 0)); |
| return -1; |
| } |
| |
| pfw = kzalloc(sizeof(osal_firmware), /*GFP_KERNEL */ GFP_ATOMIC); |
| if (!pfw) { |
| WMT_ERR_FUNC("kzalloc(%d) fail\n", sizeof(osal_firmware)); |
| return -2; |
| } |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) |
| orig_uid = cred->fsuid; |
| orig_gid = cred->fsgid; |
| cred->fsuid = cred->fsgid = 0; |
| #else |
| orig_uid = current->fsuid; |
| orig_gid = current->fsgid; |
| current->fsuid = current->fsgid = 0; |
| #endif |
| |
| set_fs(get_ds()); |
| |
| /* load patch file from fs */ |
| iRet = wmt_dev_read_file(pPatchName, &pfw->data, 0, padSzBuf); |
| set_fs(orig_fs); |
| |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)) |
| cred->fsuid = orig_uid; |
| cred->fsgid = orig_gid; |
| #else |
| current->fsuid = orig_uid; |
| current->fsgid = orig_gid; |
| #endif |
| |
| if (iRet > 0) { |
| pfw->size = iRet; |
| *ppPatch = pfw; |
| WMT_DBG_FUNC("load (%s) to addr(0x%p) success\n", pPatchName, pfw->data); |
| return 0; |
| } else { |
| kfree(pfw); |
| *ppPatch = NULL; |
| WMT_ERR_FUNC("load file (%s) fail, iRet(%d)\n", pPatchName, iRet); |
| return -1; |
| } |
| } |
| |
| |
| INT32 wmt_dev_patch_put(osal_firmware **ppPatch) |
| { |
| if (NULL != *ppPatch) { |
| if ((*ppPatch)->data) { |
| vfree((*ppPatch)->data); |
| } |
| kfree(*ppPatch); |
| *ppPatch = NULL; |
| } |
| return 0; |
| } |
| |
| |
| VOID wmt_dev_patch_info_free(VOID) |
| { |
| if (pPatchInfo) { |
| kfree(pPatchInfo); |
| pPatchInfo = NULL; |
| } |
| } |
| |
| |
| MTK_WCN_BOOL wmt_dev_is_file_exist(UCHAR *pFileName) |
| { |
| INT32 Ret = 0; |
| const struct firmware *fw = NULL; |
| |
| if(pFileName == NULL) { |
| WMT_ERR_FUNC("invalid file name pointer(%p)\n", pFileName); |
| return MTK_WCN_BOOL_FALSE; |
| } |
| |
| if (osal_strlen(pFileName) < osal_strlen(defaultPatchName)) { |
| WMT_ERR_FUNC("invalid file name(%s)\n", pFileName); |
| return MTK_WCN_BOOL_FALSE; |
| } |
| |
| Ret = request_firmware(&fw, pFileName, NULL); |
| |
| if (Ret != 0) { |
| WMT_ERR_FUNC("failed to open or read!(%s)\n", pFileName); |
| return MTK_WCN_BOOL_FALSE; |
| } |
| release_firmware(fw); |
| return true; |
| } |
| |
| static unsigned long count_last_access_sdio; |
| static unsigned long count_last_access_uart; |
| static unsigned long jiffies_last_poll; |
| |
| static INT32 wmt_dev_tra_sdio_update(void) |
| { |
| count_last_access_sdio += 1; |
| /* WMT_INFO_FUNC("jiffies_last_access_sdio: jiffies = %ul\n", jiffies); */ |
| |
| return 0; |
| } |
| |
| extern INT32 wmt_dev_tra_uart_update(void) |
| { |
| count_last_access_uart += 1; |
| /* WMT_INFO_FUNC("jiffies_last_access_uart: jiffies = %ul\n", jiffies); */ |
| |
| return 0; |
| } |
| |
| static UINT32 wmt_dev_tra_sdio_poll(void) |
| { |
| #define TIME_THRESHOLD_TO_TEMP_QUERY 3000 |
| #define COUNT_THRESHOLD_TO_TEMP_QUERY 200 |
| |
| unsigned long sdio_during_count = 0; |
| unsigned long poll_during_time = 0; |
| |
| if (jiffies > jiffies_last_poll) { |
| poll_during_time = jiffies - jiffies_last_poll; |
| } else { |
| poll_during_time = 0xffffffff; |
| } |
| |
| WMT_DBG_FUNC("**jiffies_to_mesecs(0xffffffff) = %u\n", jiffies_to_msecs(0xffffffff)); |
| |
| if (jiffies_to_msecs(poll_during_time) < TIME_THRESHOLD_TO_TEMP_QUERY) { |
| WMT_DBG_FUNC("**poll_during_time = %u < %u, not to query\n", |
| jiffies_to_msecs(poll_during_time), TIME_THRESHOLD_TO_TEMP_QUERY); |
| return -1; |
| } |
| |
| sdio_during_count = count_last_access_sdio; |
| |
| if (sdio_during_count < COUNT_THRESHOLD_TO_TEMP_QUERY) { |
| WMT_DBG_FUNC("**sdio_during_count = %lu < %u, not to query\n", |
| sdio_during_count, COUNT_THRESHOLD_TO_TEMP_QUERY); |
| return -1; |
| } |
| |
| count_last_access_sdio = 0; |
| jiffies_last_poll = jiffies; |
| |
| WMT_INFO_FUNC("**poll_during_time = %u > %u, sdio_during_count = %u > %u, query\n", |
| jiffies_to_msecs(poll_during_time), TIME_THRESHOLD_TO_TEMP_QUERY, |
| jiffies_to_msecs(sdio_during_count), COUNT_THRESHOLD_TO_TEMP_QUERY); |
| |
| return 0; |
| } |
| |
| #if 0 |
| static UINT32 wmt_dev_tra_uart_poll(void) |
| { |
| /* we not support the uart case. */ |
| return -1; |
| } |
| #endif |
| |
| INT32 wmt_dev_tm_temp_query(void) |
| { |
| #define HISTORY_NUM 5 |
| #define TEMP_THRESHOLD 65 |
| #define REFRESH_TIME 300 /* sec */ |
| |
| static INT32 temp_table[HISTORY_NUM] = { 99 }; /* not query yet. */ |
| static INT32 idx_temp_table; |
| static struct timeval query_time, now_time; |
| |
| INT8 query_cond = 0; |
| INT32 current_temp = 0; |
| INT32 index = 0; |
| |
| /* Query condition 1: */ |
| /* If we have the high temperature records on the past, we continue to query/monitor */ |
| /* the real temperature until cooling */ |
| for (index = 0; index < HISTORY_NUM; index++) { |
| if (temp_table[index] >= TEMP_THRESHOLD) { |
| query_cond = 1; |
| WMT_INFO_FUNC |
| ("high temperature (current temp = %d), we must keep querying temp temperature..\n", |
| temp_table[index]); |
| } |
| } |
| |
| do_gettimeofday(&now_time); |
| #if 1 |
| /* Query condition 2: */ |
| /* Moniter the hif_sdio activity to decide if we have the need to query temperature. */ |
| if (!query_cond) { |
| if (wmt_dev_tra_sdio_poll() == 0) { |
| query_cond = 1; |
| WMT_INFO_FUNC("sdio traffic , we must query temperature..\n"); |
| } else { |
| WMT_DBG_FUNC("sdio idle traffic ....\n"); |
| } |
| |
| /* only WIFI tx power might make temperature varies largely */ |
| #if 0 |
| if (!query_cond) { |
| last_access_time = wmt_dev_tra_uart_poll(); |
| if (jiffies_to_msecs(last_access_time) < TIME_THRESHOLD_TO_TEMP_QUERY) { |
| query_cond = 1; |
| WMT_DBG_FUNC("uart busy traffic , we must query temperature..\n"); |
| } else { |
| WMT_DBG_FUNC |
| ("uart still idle traffic , we don't query temp temperature..\n"); |
| } |
| } |
| #endif |
| } |
| #endif |
| /* Query condition 3: */ |
| /* If the query time exceeds the a certain of period, refresh temp table. */ |
| /* */ |
| if (!query_cond) { |
| if ((now_time.tv_sec < query_time.tv_sec) || /* time overflow, we refresh temp table again for simplicity! */ |
| ((now_time.tv_sec > query_time.tv_sec) && |
| (now_time.tv_sec - query_time.tv_sec) > REFRESH_TIME)) { |
| query_cond = 1; |
| |
| WMT_INFO_FUNC |
| ("It is long time (> %d sec) not to query, we must query temp temperature..\n", |
| REFRESH_TIME); |
| for (index = 0; index < HISTORY_NUM; index++) { |
| temp_table[index] = 99; |
| } |
| } |
| } |
| |
| if (query_cond) { |
| /* update the temperature record */ |
| mtk_wcn_wmt_therm_ctrl(WMTTHERM_ENABLE); |
| current_temp = mtk_wcn_wmt_therm_ctrl(WMTTHERM_READ); |
| mtk_wcn_wmt_therm_ctrl(WMTTHERM_DISABLE); |
| wmt_lib_notify_stp_sleep(); |
| idx_temp_table = (idx_temp_table + 1) % HISTORY_NUM; |
| temp_table[idx_temp_table] = current_temp; |
| do_gettimeofday(&query_time); |
| |
| WMT_INFO_FUNC("[Thermal] current_temp = 0x%x\n", (current_temp & 0xFF)); |
| } else { |
| current_temp = temp_table[idx_temp_table]; |
| idx_temp_table = (idx_temp_table + 1) % HISTORY_NUM; |
| temp_table[idx_temp_table] = current_temp; |
| } |
| |
| /* */ |
| /* Dump information */ |
| /* */ |
| WMT_DBG_FUNC("[Thermal] idx_temp_table = %d\n", idx_temp_table); |
| WMT_DBG_FUNC("[Thermal] now.time = %ld, query.time = %ld, REFRESH_TIME = %d\n", |
| now_time.tv_sec, query_time.tv_sec, REFRESH_TIME); |
| |
| WMT_DBG_FUNC("[0] = %d, [1] = %d, [2] = %d, [3] = %d, [4] = %d\n----\n", |
| temp_table[0], temp_table[1], temp_table[2], temp_table[3], temp_table[4]); |
| |
| return current_temp; |
| } |
| |
| ssize_t WMT_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) |
| { |
| INT32 iRet = 0; |
| UCHAR wrBuf[NAME_MAX + 1] = { 0 }; |
| INT32 copySize = (count < NAME_MAX) ? count : NAME_MAX; |
| |
| WMT_LOUD_FUNC("count:%d copySize:%d\n", count, copySize); |
| |
| if (copySize > 0) { |
| if (copy_from_user(wrBuf, buf, copySize)) { |
| iRet = -EFAULT; |
| goto write_done; |
| } |
| iRet = copySize; |
| wrBuf[NAME_MAX] = '\0'; |
| |
| if (!strncasecmp(wrBuf, "ok", NAME_MAX)) { |
| WMT_DBG_FUNC("resp str ok\n"); |
| /* pWmtDevCtx->cmd_result = 0; */ |
| wmt_lib_trigger_cmd_signal(0); |
| } else { |
| WMT_WARN_FUNC("warning resp str (%s)\n", wrBuf); |
| /* pWmtDevCtx->cmd_result = -1; */ |
| wmt_lib_trigger_cmd_signal(-1); |
| } |
| /* complete(&pWmtDevCtx->cmd_comp); */ |
| |
| } |
| |
| write_done: |
| return iRet; |
| } |
| |
| ssize_t WMT_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) |
| { |
| INT32 iRet = 0; |
| PUCHAR pCmd = NULL; |
| UINT32 cmdLen = 0; |
| pCmd = wmt_lib_get_cmd(); |
| |
| if (pCmd != NULL) { |
| cmdLen = osal_strlen(pCmd) < NAME_MAX ? osal_strlen(pCmd) : NAME_MAX; |
| WMT_DBG_FUNC("cmd str(%s)\n", pCmd); |
| if (copy_to_user(buf, pCmd, cmdLen)) { |
| iRet = -EFAULT; |
| } else { |
| iRet = cmdLen; |
| } |
| } |
| #if 0 |
| if (test_and_clear_bit(WMT_STAT_CMD, &pWmtDevCtx->state)) { |
| iRet = osal_strlen(localBuf) < NAME_MAX ? osal_strlen(localBuf) : NAME_MAX; |
| /* we got something from STP driver */ |
| WMT_DBG_FUNC("copy cmd to user by read:%s\n", localBuf); |
| if (copy_to_user(buf, localBuf, iRet)) { |
| iRet = -EFAULT; |
| goto read_done; |
| } |
| } |
| #endif |
| return iRet; |
| } |
| |
| unsigned int WMT_poll(struct file *filp, poll_table *wait) |
| { |
| UINT32 mask = 0; |
| P_OSAL_EVENT pEvent = wmt_lib_get_cmd_event(); |
| |
| poll_wait(filp, &pEvent->waitQueue, wait); |
| /* empty let select sleep */ |
| if (MTK_WCN_BOOL_TRUE == wmt_lib_get_cmd_status()) { |
| mask |= POLLIN | POLLRDNORM; /* readable */ |
| } |
| #if 0 |
| if (test_bit(WMT_STAT_CMD, &pWmtDevCtx->state)) { |
| mask |= POLLIN | POLLRDNORM; /* readable */ |
| } |
| #endif |
| mask |= POLLOUT | POLLWRNORM; /* writable */ |
| return mask; |
| } |
| |
| /* INT32 WMT_ioctl(struct inode *inode, struct file *filp, UINT32 cmd, unsigned long arg) */ |
| long WMT_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
| { |
| #define WMT_IOC_MAGIC 0xa0 |
| #define WMT_IOCTL_SET_PATCH_NAME _IOW(WMT_IOC_MAGIC, 4, char*) |
| #define WMT_IOCTL_SET_STP_MODE _IOW(WMT_IOC_MAGIC, 5, int) |
| #define WMT_IOCTL_FUNC_ONOFF_CTRL _IOW(WMT_IOC_MAGIC, 6, int) |
| #define WMT_IOCTL_LPBK_POWER_CTRL _IOW(WMT_IOC_MAGIC, 7, int) |
| #define WMT_IOCTL_LPBK_TEST _IOWR(WMT_IOC_MAGIC, 8, char*) |
| #define WMT_IOCTL_GET_CHIP_INFO _IOR(WMT_IOC_MAGIC, 12, int) |
| #define WMT_IOCTL_SET_LAUNCHER_KILL _IOW(WMT_IOC_MAGIC, 13, int) |
| #define WMT_IOCTL_SET_PATCH_NUM _IOW(WMT_IOC_MAGIC, 14, int) |
| #define WMT_IOCTL_SET_PATCH_INFO _IOW(WMT_IOC_MAGIC, 15, char*) |
| #define WMT_IOCTL_PORT_NAME _IOWR(WMT_IOC_MAGIC, 20, char*) |
| #define WMT_IOCTL_WMT_CFG_NAME _IOWR(WMT_IOC_MAGIC, 21, char*) |
| #define WMT_IOCTL_WMT_QUERY_CHIPID _IOR(WMT_IOC_MAGIC, 22, int) |
| #define WMT_IOCTL_WMT_TELL_CHIPID _IOW(WMT_IOC_MAGIC, 23, int) |
| #define WMT_IOCTL_WMT_COREDUMP_CTRL _IOW(WMT_IOC_MAGIC, 24, int) |
| |
| |
| |
| INT32 iRet = 0; |
| UCHAR pBuffer[NAME_MAX + 1]; |
| WMT_DBG_FUNC("cmd (%u), arg (0x%lx)\n", cmd, arg); |
| switch (cmd) { |
| case WMT_IOCTL_SET_PATCH_NAME: /* patch location */ |
| { |
| |
| if (copy_from_user(pBuffer, (void *)arg, NAME_MAX)) { |
| iRet = -EFAULT; |
| break; |
| } |
| pBuffer[NAME_MAX] = '\0'; |
| wmt_lib_set_patch_name(pBuffer); |
| } |
| break; |
| |
| case WMT_IOCTL_SET_STP_MODE: /* stp/hif/fm mode */ |
| |
| /* set hif conf */ |
| do { |
| P_OSAL_OP pOp; |
| MTK_WCN_BOOL bRet; |
| P_OSAL_SIGNAL pSignal = NULL; |
| P_WMT_HIF_CONF pHif = NULL; |
| |
| iRet = wmt_lib_set_hif(arg); |
| if (0 != iRet) { |
| WMT_INFO_FUNC("wmt_lib_set_hif fail (%lu)\n", arg); |
| break; |
| } |
| |
| pOp = wmt_lib_get_free_op(); |
| if (!pOp) { |
| WMT_INFO_FUNC("get_free_lxop fail\n"); |
| break; |
| } |
| pSignal = &pOp->signal; |
| pOp->op.opId = WMT_OPID_HIF_CONF; |
| |
| pHif = wmt_lib_get_hif(); |
| |
| osal_memcpy(&pOp->op.au4OpData[0], pHif, sizeof(WMT_HIF_CONF)); |
| pOp->op.u4InfoBit = WMT_OP_HIF_BIT; |
| pSignal->timeoutValue = 0; |
| |
| bRet = wmt_lib_put_act_op(pOp); |
| WMT_DBG_FUNC("WMT_OPID_HIF_CONF result(%d)\n", bRet); |
| iRet = (MTK_WCN_BOOL_FALSE == bRet) ? -EFAULT : 0; |
| } while (0); |
| |
| break; |
| |
| case WMT_IOCTL_FUNC_ONOFF_CTRL: /* test turn on/off func */ |
| |
| do { |
| MTK_WCN_BOOL bRet = MTK_WCN_BOOL_FALSE; |
| if (arg & 0x80000000) { |
| bRet = mtk_wcn_wmt_func_on(arg & 0xF); |
| } else { |
| bRet = mtk_wcn_wmt_func_off(arg & 0xF); |
| } |
| iRet = (MTK_WCN_BOOL_FALSE == bRet) ? -EFAULT : 0; |
| } while (0); |
| |
| break; |
| |
| case WMT_IOCTL_LPBK_POWER_CTRL: |
| /*switch Loopback function on/off |
| arg: bit0 = 1:turn loopback function on |
| bit0 = 0:turn loopback function off |
| */ |
| do { |
| MTK_WCN_BOOL bRet = MTK_WCN_BOOL_FALSE; |
| if (arg & 0x01) { |
| bRet = mtk_wcn_wmt_func_on(WMTDRV_TYPE_LPBK); |
| } else { |
| bRet = mtk_wcn_wmt_func_off(WMTDRV_TYPE_LPBK); |
| } |
| iRet = (MTK_WCN_BOOL_FALSE == bRet) ? -EFAULT : 0; |
| } while (0); |
| |
| |
| break; |
| |
| |
| case WMT_IOCTL_LPBK_TEST: |
| do { |
| P_OSAL_OP pOp; |
| MTK_WCN_BOOL bRet; |
| UINT32 u4Wait; |
| /* UINT8 lpbk_buf[1024] = {0}; */ |
| UINT32 effectiveLen = 0; |
| P_OSAL_SIGNAL pSignal = NULL; |
| |
| if (copy_from_user(&effectiveLen, (void *)arg, sizeof(effectiveLen))) { |
| iRet = -EFAULT; |
| WMT_ERR_FUNC("copy_from_user failed at %d\n", __LINE__); |
| break; |
| } |
| if (effectiveLen > sizeof(gLpbkBuf)) { |
| iRet = -EFAULT; |
| WMT_ERR_FUNC("length is too long\n"); |
| break; |
| } |
| WMT_DBG_FUNC("len = %d\n", effectiveLen); |
| |
| pOp = wmt_lib_get_free_op(); |
| if (!pOp) { |
| WMT_WARN_FUNC("get_free_lxop fail\n"); |
| iRet = -EFAULT; |
| break; |
| } |
| u4Wait = 2000; |
| if (copy_from_user |
| (&gLpbkBuf[0], (void *)arg + sizeof(unsigned long), effectiveLen)) { |
| WMT_ERR_FUNC("copy_from_user failed at %d\n", __LINE__); |
| iRet = -EFAULT; |
| break; |
| } |
| pSignal = &pOp->signal; |
| pOp->op.opId = WMT_OPID_LPBK; |
| pOp->op.au4OpData[0] = effectiveLen; /* packet length */ |
| pOp->op.au4OpData[1] = (UINT32) &gLpbkBuf[0]; |
| memcpy(&gLpbkBufLog, |
| &gLpbkBuf[((effectiveLen >= 4) ? effectiveLen - 4 : 0)], 4); |
| pSignal->timeoutValue = MAX_EACH_WMT_CMD; |
| WMT_INFO_FUNC("OPID(%d) type(%d) start\n", |
| pOp->op.opId, pOp->op.au4OpData[0]); |
| if (DISABLE_PSM_MONITOR()) { |
| WMT_ERR_FUNC("wake up failed\n"); |
| wmt_lib_put_op_to_free_queue(pOp); |
| return -1; |
| } |
| |
| bRet = wmt_lib_put_act_op(pOp); |
| ENABLE_PSM_MONITOR(); |
| if (MTK_WCN_BOOL_FALSE == bRet) { |
| WMT_WARN_FUNC("OPID(%d) type(%d) buf tail(0x%08x) fail\n", |
| pOp->op.opId, pOp->op.au4OpData[0], gLpbkBufLog); |
| iRet = -1; |
| break; |
| } else { |
| WMT_INFO_FUNC("OPID(%d) length(%d) ok\n", |
| pOp->op.opId, pOp->op.au4OpData[0]); |
| iRet = pOp->op.au4OpData[0]; |
| if (copy_to_user |
| ((void *)arg + sizeof(ULONG) + sizeof(UCHAR[2048]), gLpbkBuf, |
| iRet)) { |
| iRet = -EFAULT; |
| break; |
| } |
| } |
| } while (0); |
| |
| break; |
| #if 0 |
| case 9: |
| { |
| #define LOG_BUF_SZ 300 |
| UCHAR buf[LOG_BUF_SZ]; |
| INT32 len = 0; |
| INT32 remaining = 0; |
| |
| remaining = mtk_wcn_stp_btm_get_dmp(buf, &len); |
| |
| if (remaining == 0) { |
| WMT_DBG_FUNC("waiting dmp\n"); |
| wait_event_interruptible(dmp_wq, dmp_flag != 0); |
| dmp_flag = 0; |
| remaining = mtk_wcn_stp_btm_get_dmp(buf, &len); |
| |
| /* WMT_INFO_FUNC("len = %d ###%s#\n", len, buf); */ |
| } else { |
| WMT_LOUD_FUNC("no waiting dmp\n"); |
| } |
| |
| if (unlikely((len + sizeof(INT32)) >= LOG_BUF_SZ)) { |
| WMT_ERR_FUNC("len is larger buffer\n"); |
| iRet = -EFAULT; |
| goto fail_exit; |
| } |
| |
| buf[sizeof(INT32) + len] = '\0'; |
| |
| if (copy_to_user((void *)arg, (UCHAR *) &len, sizeof(INT32))) { |
| iRet = -EFAULT; |
| goto fail_exit; |
| } |
| |
| if (copy_to_user((void *)arg + sizeof(INT32), buf, len)) { |
| iRet = -EFAULT; |
| goto fail_exit; |
| } |
| } |
| break; |
| |
| case 10: |
| { |
| WMT_INFO_FUNC("Enable combo trace32 dump\n"); |
| wmt_cdev_t32dmp_enable(); |
| WMT_INFO_FUNC("Enable STP debugging mode\n"); |
| mtk_wcn_stp_dbg_enable(); |
| } |
| break; |
| |
| case 11: |
| { |
| WMT_INFO_FUNC("Disable combo trace32 dump\n"); |
| wmt_cdev_t32dmp_disable(); |
| WMT_INFO_FUNC("Disable STP debugging mode\n"); |
| mtk_wcn_stp_dbg_disable(); |
| } |
| break; |
| #endif |
| |
| case 10: |
| { |
| wmt_lib_host_awake_get(); |
| mtk_wcn_stp_coredump_start_ctrl(1); |
| osal_strcpy(pBuffer, "MT662x f/w coredump start-"); |
| if (copy_from_user |
| (pBuffer + osal_strlen(pBuffer), (void *)arg, |
| NAME_MAX - osal_strlen(pBuffer))) { |
| /* osal_strcpy(pBuffer, "MT662x f/w assert core dump start"); */ |
| WMT_ERR_FUNC("copy assert string failed\n"); |
| } |
| pBuffer[NAME_MAX] = '\0'; |
| osal_dbg_assert_aee(pBuffer, pBuffer); |
| } |
| break; |
| case 11: |
| { |
| osal_dbg_assert_aee("MT662x f/w coredump end", |
| "MT662x firmware coredump ends"); |
| wmt_lib_host_awake_put(); |
| } |
| break; |
| |
| |
| case WMT_IOCTL_GET_CHIP_INFO: |
| { |
| if (0 == arg) { |
| return wmt_lib_get_icinfo(WMTCHIN_CHIPID); |
| } else if (1 == arg) { |
| return wmt_lib_get_icinfo(WMTCHIN_HWVER); |
| } else if (2 == arg) { |
| return wmt_lib_get_icinfo(WMTCHIN_FWVER); |
| } |
| } |
| break; |
| |
| case WMT_IOCTL_SET_LAUNCHER_KILL:{ |
| if (1 == arg) { |
| WMT_INFO_FUNC("launcher may be killed,block abnormal stp tx.\n"); |
| wmt_lib_set_stp_wmt_last_close(1); |
| } else { |
| wmt_lib_set_stp_wmt_last_close(0); |
| } |
| |
| } |
| break; |
| |
| case WMT_IOCTL_SET_PATCH_NUM:{ |
| UINT32 MAX_UINT = ~0; |
| UINT32 t_patchnum = arg; |
| |
| if (t_patchnum <= 0) { |
| WMT_ERR_FUNC("patch num <= 0!\n"); |
| break; |
| } |
| |
| /* Verify that the amount of slots requested wont overflow */ |
| if (t_patchnum >= (MAX_UINT / sizeof(WMT_PATCH_INFO))) { |
| WMT_ERR_FUNC("Patch num is too large!\n"); |
| break; |
| } |
| |
| pPatchInfo = kcalloc(t_patchnum, sizeof(WMT_PATCH_INFO), GFP_ATOMIC); |
| if (!pPatchInfo) { |
| WMT_ERR_FUNC("allocate memory fail!\n"); |
| break; |
| } |
| pAtchNum = t_patchnum; |
| WMT_INFO_FUNC("get patch num from launcher = %d\n", pAtchNum); |
| wmt_lib_set_patch_num(pAtchNum); |
| } |
| break; |
| |
| case WMT_IOCTL_SET_PATCH_INFO:{ |
| WMT_PATCH_INFO wMtPatchInfo; |
| UINT32 dWloadSeq; |
| static UINT32 counter; |
| |
| if (!pPatchInfo) { |
| WMT_ERR_FUNC("NULL patch info pointer\n"); |
| break; |
| } |
| |
| if (copy_from_user(&wMtPatchInfo, (void *)arg, sizeof(WMT_PATCH_INFO))) { |
| WMT_ERR_FUNC("copy_from_user failed at %d\n", __LINE__); |
| iRet = -EFAULT; |
| break; |
| } |
| if (wMtPatchInfo.dowloadSeq > pAtchNum) { |
| WMT_ERR_FUNC("dowloadSeq would overflow\n"); |
| iRet = -EFAULT; |
| break; |
| } |
| dWloadSeq = wMtPatchInfo.dowloadSeq; |
| |
| wMtPatchInfo.patchName[sizeof(wMtPatchInfo.patchName)-1] = '\0'; |
| WMT_DBG_FUNC |
| ("current download seq no is %d,patch name is %s,addres info is 0x%02x,0x%02x,0x%02x,0x%02x\n", |
| dWloadSeq, wMtPatchInfo.patchName, wMtPatchInfo.addRess[0], |
| wMtPatchInfo.addRess[1], wMtPatchInfo.addRess[2], |
| wMtPatchInfo.addRess[3]); |
| if (dWloadSeq <= pAtchNum) { |
| osal_memcpy(pPatchInfo + dWloadSeq - 1, &wMtPatchInfo, |
| sizeof(WMT_PATCH_INFO)); |
| |
| if (++counter == pAtchNum) { |
| wmt_lib_set_patch_info(pPatchInfo); |
| counter = 0; |
| } |
| } |
| |
| } |
| break; |
| |
| case WMT_IOCTL_PORT_NAME:{ |
| CHAR cUartName[NAME_MAX + 1]; |
| if (copy_from_user(cUartName, (void *)arg, NAME_MAX)) { |
| iRet = -EFAULT; |
| break; |
| } |
| cUartName[NAME_MAX] = '\0'; |
| wmt_lib_set_uart_name(cUartName); |
| } |
| break; |
| |
| case WMT_IOCTL_WMT_CFG_NAME: |
| { |
| CHAR cWmtCfgName[NAME_MAX + 1]; |
| if (copy_from_user(cWmtCfgName, (void *)arg, NAME_MAX)) { |
| iRet = -EFAULT; |
| break; |
| } |
| cWmtCfgName[NAME_MAX] = '\0'; |
| wmt_conf_set_cfg_file(cWmtCfgName); |
| } |
| break; |
| case WMT_IOCTL_WMT_QUERY_CHIPID: |
| { |
| #if !(DELETE_HIF_SDIO_CHRDEV) |
| iRet = mtk_wcn_hif_sdio_query_chipid(1); |
| #else |
| iRet = mtk_wcn_wmt_chipid_query(); |
| #endif |
| } |
| break; |
| case WMT_IOCTL_WMT_TELL_CHIPID: |
| { |
| #if !(DELETE_HIF_SDIO_CHRDEV) |
| iRet = mtk_wcn_hif_sdio_tell_chipid(arg); |
| #endif |
| |
| if (0x6628 == arg || 0x6630 == arg) { |
| wmt_lib_merge_if_flag_ctrl(1); |
| } else { |
| wmt_lib_merge_if_flag_ctrl(0); |
| } |
| } |
| break; |
| case WMT_IOCTL_WMT_COREDUMP_CTRL: |
| { |
| if (0 == arg) { |
| mtk_wcn_stp_coredump_flag_ctrl(0); |
| } else { |
| mtk_wcn_stp_coredump_flag_ctrl(1); |
| } |
| } |
| break; |
| default: |
| iRet = -EINVAL; |
| WMT_WARN_FUNC("unknown cmd (%d)\n", cmd); |
| break; |
| } |
| |
| |
| return iRet; |
| } |
| |
| static int WMT_open(struct inode *inode, struct file *file) |
| { |
| LONG ret; |
| |
| WMT_INFO_FUNC("major %d minor %d (pid %d)\n", imajor(inode), iminor(inode), current->pid); |
| if (0 == gWmtInitDone) { |
| ret = |
| wait_event_timeout(gWmtInitWq, gWmtInitDone != 0, |
| msecs_to_jiffies(WMT_DEV_INIT_TO_MS)); |
| if (!ret) { |
| WMT_WARN_FUNC("wait_event_timeout (%d)ms,(%ld)jiffies,return -EIO\n", |
| WMT_DEV_INIT_TO_MS, msecs_to_jiffies(WMT_DEV_INIT_TO_MS)); |
| return -EIO; |
| } |
| } |
| if (atomic_inc_return(&gWmtRefCnt) == 1) { |
| WMT_INFO_FUNC("1st call\n"); |
| } |
| |
| return 0; |
| } |
| |
| static int WMT_close(struct inode *inode, struct file *file) |
| { |
| WMT_INFO_FUNC("major %d minor %d (pid %d)\n", imajor(inode), iminor(inode), current->pid); |
| |
| if (atomic_dec_return(&gWmtRefCnt) == 0) { |
| WMT_INFO_FUNC("last call\n"); |
| } |
| |
| return 0; |
| } |
| |
| struct file_operations gWmtFops = { |
| .open = WMT_open, |
| .release = WMT_close, |
| .read = WMT_read, |
| .write = WMT_write, |
| /* .ioctl = WMT_ioctl, */ |
| .unlocked_ioctl = WMT_unlocked_ioctl, |
| .poll = WMT_poll, |
| }; |
| |
| #if REMOVE_MK_NODE |
| struct class *wmt_class = NULL; |
| #endif |
| |
| |
| |
| static int WMT_init(void) |
| { |
| dev_t devID = MKDEV(gWmtMajor, 0); |
| INT32 cdevErr = -1; |
| INT32 ret = -1; |
| #if REMOVE_MK_NODE |
| struct device *wmt_dev = NULL; |
| #endif |
| |
| WMT_INFO_FUNC("WMT Version= %s DATE=%s\n", MTK_WMT_VERSION, MTK_WMT_DATE); |
| WMT_INFO_FUNC("COMBO Driver Version= %s\n", MTK_COMBO_DRIVER_VERSION); |
| /* Prepare a UCHAR device */ |
| /*static allocate chrdev */ |
| gWmtInitDone = 0; |
| init_waitqueue_head((wait_queue_head_t *) &gWmtInitWq); |
| |
| stp_drv_init(); |
| |
| ret = register_chrdev_region(devID, WMT_DEV_NUM, WMT_DRIVER_NAME); |
| if (ret) { |
| WMT_ERR_FUNC("fail to register chrdev\n"); |
| return ret; |
| } |
| |
| cdev_init(&gWmtCdev, &gWmtFops); |
| gWmtCdev.owner = THIS_MODULE; |
| |
| cdevErr = cdev_add(&gWmtCdev, devID, WMT_DEV_NUM); |
| if (cdevErr) { |
| WMT_ERR_FUNC("cdev_add() fails (%d)\n", cdevErr); |
| goto error; |
| } |
| WMT_INFO_FUNC("driver(major %d) installed\n", gWmtMajor); |
| #if REMOVE_MK_NODE |
| wmt_class = class_create(THIS_MODULE, "stpwmt"); |
| if (IS_ERR(wmt_class)) |
| goto error; |
| wmt_dev = device_create(wmt_class, NULL, devID, NULL, "stpwmt"); |
| if (IS_ERR(wmt_dev)) |
| goto error; |
| #endif |
| |
| #if 0 |
| pWmtDevCtx = wmt_drv_create(); |
| if (!pWmtDevCtx) { |
| WMT_ERR_FUNC("wmt_drv_create() fails\n"); |
| goto error; |
| } |
| |
| ret = wmt_drv_init(pWmtDevCtx); |
| if (ret) { |
| WMT_ERR_FUNC("wmt_drv_init() fails (%d)\n", ret); |
| goto error; |
| } |
| |
| WMT_INFO_FUNC("stp_btmcb_reg\n"); |
| wmt_cdev_btmcb_reg(); |
| |
| ret = wmt_drv_start(pWmtDevCtx); |
| if (ret) { |
| WMT_ERR_FUNC("wmt_drv_start() fails (%d)\n", ret); |
| goto error; |
| } |
| #endif |
| ret = wmt_lib_init(); |
| if (ret) { |
| WMT_ERR_FUNC("wmt_lib_init() fails (%d)\n", ret); |
| goto error; |
| } |
| #if CFG_WMT_DBG_SUPPORT |
| wmt_dev_dbg_setup(); |
| #endif |
| |
| #if CFG_WMT_PROC_FOR_AEE |
| wmt_dev_proc_for_aee_setup(); |
| #endif |
| |
| mtk_wcn_hif_sdio_update_cb_reg(wmt_dev_tra_sdio_update); |
| |
| gWmtInitDone = 1; |
| wake_up(&gWmtInitWq); |
| |
| WMT_INFO_FUNC("success\n"); |
| return 0; |
| |
| error: |
| wmt_lib_deinit(); |
| #if CFG_WMT_DBG_SUPPORT |
| wmt_dev_dbg_remove(); |
| #endif |
| #if REMOVE_MK_NODE |
| if (!IS_ERR(wmt_dev)) |
| device_destroy(wmt_class, devID); |
| if (!IS_ERR(wmt_class)) { |
| class_destroy(wmt_class); |
| wmt_class = NULL; |
| } |
| #endif |
| |
| if (cdevErr == 0) { |
| cdev_del(&gWmtCdev); |
| } |
| |
| if (ret == 0) { |
| unregister_chrdev_region(devID, WMT_DEV_NUM); |
| gWmtMajor = -1; |
| } |
| |
| WMT_ERR_FUNC("fail\n"); |
| |
| return -1; |
| } |
| |
| static void WMT_exit(void) |
| { |
| dev_t dev = MKDEV(gWmtMajor, 0); |
| |
| |
| wmt_lib_deinit(); |
| |
| #if CFG_WMT_DBG_SUPPORT |
| wmt_dev_dbg_remove(); |
| #endif |
| |
| #if CFG_WMT_PROC_FOR_AEE |
| wmt_dev_proc_for_aee_remove(); |
| #endif |
| |
| cdev_del(&gWmtCdev); |
| unregister_chrdev_region(dev, WMT_DEV_NUM); |
| gWmtMajor = -1; |
| #if REMOVE_MK_NODE |
| device_destroy(wmt_class, MKDEV(gWmtMajor, 0)); |
| class_destroy(wmt_class); |
| wmt_class = NULL; |
| #endif |
| #ifdef MTK_WMT_WAKELOCK_SUPPORT |
| WMT_WARN_FUNC("destroy func_on_off_wake_lock\n"); |
| wake_lock_destroy(&func_on_off_wake_lock); |
| #endif |
| |
| stp_drv_exit(); |
| |
| WMT_INFO_FUNC("done\n"); |
| } |
| |
| #ifdef MTK_WCN_REMOVE_KERNEL_MODULE |
| |
| int mtk_wcn_combo_common_drv_init(void) |
| { |
| return WMT_init(); |
| |
| } |
| |
| void mtk_wcn_combo_common_drv_exit(void) |
| { |
| return WMT_exit(); |
| } |
| |
| |
| EXPORT_SYMBOL(mtk_wcn_combo_common_drv_init); |
| EXPORT_SYMBOL(mtk_wcn_combo_common_drv_exit); |
| #else |
| module_init(WMT_init); |
| module_exit(WMT_exit); |
| #endif |
| /* MODULE_LICENSE("Proprietary"); */ |
| MODULE_LICENSE("GPL"); |
| MODULE_AUTHOR("MediaTek Inc WCN"); |
| MODULE_DESCRIPTION("MTK WCN combo driver for WMT function"); |
| |
| module_param(gWmtMajor, uint, 0); |