/* * 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. */ /*! \file * \brief Declaration of library functions * Any definitions in this file will be shared among GLUE Layer and internal Driver Stack. */ /******************************************************************************* * C O M P I L E R F L A G S ******************************************************************************** */ #define pr_fmt(fmt) KBUILD_MODNAME "@(%s:%d) " fmt, __func__, __LINE__ /******************************************************************************* * 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 #include #include #include #include #include #include #include #include #include #include "connectivity_build_in_adapter.h" #include "osal.h" /******************************************************************************* * C O N S T A N T S ******************************************************************************** */ #define GPIO_ASSERT 70 /******************************************************************************* * 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 ******************************************************************************** */ /* CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ static unsigned short const crc16_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; int ftrace_flag = 1; static unsigned long __read_mostly mark_addr; static unsigned int g_pid; /******************************************************************************* * 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 ******************************************************************************** */ /*string operations*/ unsigned int osal_strlen(const char *str) { return strlen(str); } int osal_strcmp(const char *dst, const char *src) { return strcmp(dst, src); } int osal_strncmp(const char *dst, const char *src, unsigned int len) { return strncmp(dst, src, len); } char *osal_strcpy(char *dst, const char *src) { return strncpy(dst, src, strlen(src)+1); } char *osal_strncpy(char *dst, const char *src, unsigned int len) { return strncpy(dst, src, len); } char *osal_strcat(char *dst, const char *src) { return strncat(dst, src, strlen(src)); } char *osal_strncat(char *dst, const char *src, unsigned int len) { return strncat(dst, src, len); } char *osal_strchr(const char *str, unsigned char c) { return strchr(str, c); } char *osal_strsep(char **str, const char *c) { return strsep(str, c); } int osal_strtol(const char *str, unsigned int adecimal, long *res) { if (sizeof(long) == 4) return kstrtou32(str, adecimal, (unsigned int *) res); else return kstrtol(str, adecimal, res); } char *osal_strstr(char *str1, const char *str2) { return strstr(str1, str2); } char *osal_strnstr(char *str1, const char *str2, int n) { return strnstr(str1, str2, n); } void osal_bug_on(unsigned int val) { WARN_ON(val); } int osal_snprintf(char *buf, unsigned int len, const char *fmt, ...) { int iRet = 0; va_list args; /*va_start(args, fmt); */ va_start(args, fmt); /*iRet = snprintf(buf, len, fmt, args); */ iRet = vsnprintf(buf, len, fmt, args); va_end(args); return iRet; } int osal_sprintf(char *str, const char *format, ...) { int iRet = 0; va_list args; va_start(args, format); iRet = vsnprintf(str, DBG_LOG_STR_SIZE, format, args); va_end(args); return iRet; } void *osal_malloc(unsigned int size) { return vmalloc(size); } void osal_free(const void *dst) { vfree(dst); } void *osal_memset(void *buf, int i, unsigned int len) { return memset(buf, i, len); } void *osal_memcpy(void *dst, const void *src, unsigned int len) { return memcpy(dst, src, len); } void osal_memcpy_fromio(void *dst, const void *src, unsigned int len) { return memcpy_fromio(dst, src, len); } void osal_memcpy_toio(void *dst, const void *src, unsigned int len) { return memcpy_toio(dst, src, len); } int osal_memcmp(const void *buf1, const void *buf2, unsigned int len) { return memcmp(buf1, buf2, len); } unsigned short osal_crc16(const unsigned char *buffer, const unsigned int length) { unsigned short crc = 0; unsigned int i = 0; const unsigned char *temp = buffer; /* FIXME: Add STP checksum feature */ crc = 0; for (i = 0; i < length; i++, temp++) crc = (crc >> 8) ^ crc16_table[(crc ^ (*temp)) & 0xff]; return crc; } void osal_dump_thread_state(const unsigned char *name) { //return connectivity_export_dump_thread_state(name); } static inline bool __osal_is_valid_thread(P_OSAL_THREAD pThread) { if ((pThread) && !IS_ERR_OR_NULL(pThread->pThread)) return true; else return false; } void osal_thread_show_stack(P_OSAL_THREAD pThread) { if (__osal_is_valid_thread(pThread)) KERNEL_show_stack(pThread->pThread, NULL); } /* * OSAL layer Thread Opeartion related APIs * */ int osal_thread_create(P_OSAL_THREAD pThread) { struct task_struct *task; if (!pThread) return -1; task = kthread_create(pThread->pThreadFunc, pThread->pThreadData, pThread->threadName); if (IS_ERR(task)) { pr_err("[%s] create %s thread fail", __func__, pThread->threadName); return -1; } pThread->pThread = task; return 0; } int osal_thread_run(P_OSAL_THREAD pThread) { if (__osal_is_valid_thread(pThread)) { wake_up_process(pThread->pThread); return 0; } else { return -1; } } int osal_thread_stop(P_OSAL_THREAD pThread) { int iRet; if (__osal_is_valid_thread(pThread)) { iRet = kthread_stop(pThread->pThread); pThread->pThread = NULL; return iRet; } return -1; } int osal_thread_should_stop(P_OSAL_THREAD pThread) { if (__osal_is_valid_thread(pThread)) return kthread_should_stop(); else return 1; } int osal_thread_wait_for_event(P_OSAL_THREAD pThread, P_OSAL_EVENT pEvent, P_OSAL_EVENT_CHECKER pChecker) { /* P_DEV_WMT pDevWmt;*/ if (__osal_is_valid_thread(pThread) && (pEvent) && (pChecker)) { return wait_event_interruptible(pEvent->waitQueue, ( osal_thread_should_stop(pThread) || (*pChecker) (pThread))); } return -1; } int osal_thread_destroy(P_OSAL_THREAD pThread) { if (__osal_is_valid_thread(pThread)) { kthread_stop(pThread->pThread); pThread->pThread = NULL; } return 0; } /* * osal_thread_sched_retrieve * Retrieve thread's current scheduling statistics and stored in output "sched". * Return value: * 0 : Schedstats successfully retrieved * -1 : Kernel's schedstats feature not enabled * -2 : pThread not yet initialized or sched is a NULL pointer */ static int osal_thread_sched_retrieve(P_OSAL_THREAD pThread, P_OSAL_THREAD_SCHEDSTATS sched) { #ifdef CONFIG_SCHEDSTATS struct sched_entity se; unsigned long long sec; unsigned long usec; if (!sched) return -2; /* always clear sched to simplify error handling at caller side */ memset(sched, 0, sizeof(OSAL_THREAD_SCHEDSTATS)); if (!__osal_is_valid_thread(pThread)) return -2; memcpy(&se, &pThread->pThread->se, sizeof(struct sched_entity)); osal_get_local_time(&sec, &usec); sched->time = sec*1000 + usec/1000; sched->exec = se.sum_exec_runtime; sched->runnable = se.statistics.wait_sum; sched->iowait = se.statistics.iowait_sum; return 0; #else /* always clear sched to simplify error handling at caller side */ if (sched) memset(sched, 0, sizeof(OSAL_THREAD_SCHEDSTATS)); return -1; #endif } /* * osal_thread_sched_mark * Record the thread's current schedstats and stored * in output "schedstats" parameter for profiling at * later time. * Return value: * 0 : Schedstats successfully recorded * -1 : Kernel's schedstats feature not enabled * -2 : pThread not yet initialized or invalid parameters */ int osal_thread_sched_mark(P_OSAL_THREAD pThread, P_OSAL_THREAD_SCHEDSTATS schedstats) { return osal_thread_sched_retrieve(pThread, schedstats); } /* * osal_thread_sched_unmark * Calculate scheduling statistics against the previously marked point. * The result will be filled back into the schedstats output parameter. * Return value: * 0 : Schedstats successfully calculated * -1 : Kernel's schedstats feature not enabled * -2 : pThread not yet initialized or invalid parameters */ int osal_thread_sched_unmark(P_OSAL_THREAD pThread, P_OSAL_THREAD_SCHEDSTATS schedstats) { int ret; OSAL_THREAD_SCHEDSTATS sched_now; if (unlikely(!schedstats)) { ret = -2; } else { ret = osal_thread_sched_retrieve(pThread, &sched_now); if (ret == 0) { schedstats->time = sched_now.time - schedstats->time; schedstats->exec = sched_now.exec - schedstats->exec; schedstats->runnable = sched_now.runnable - schedstats->runnable; schedstats->iowait = sched_now.iowait - schedstats->iowait; } } return ret; } /* * OSAL layer Signal Opeartion related APIs * initialization * wait for signal * wait for signal timerout * raise signal * destroy a signal * */ int osal_signal_init(P_OSAL_SIGNAL pSignal) { if (pSignal) { init_completion(&pSignal->comp); return 0; } else { return -1; } } int osal_wait_for_signal(P_OSAL_SIGNAL pSignal) { if (pSignal) { wait_for_completion_interruptible(&pSignal->comp); return 0; } else { return -1; } } /* * osal_wait_for_signal_timeout * * Wait for a signal to be triggered by the corresponding thread, within the * expected timeout specified by the signal's timeoutValue. * When the pThread parameter is specified, the thread's scheduling ability is * considered, the timeout will be extended when thread cannot acquire CPU * resource, and will only extend for a number of times specified by the * signal's timeoutExtension should the situation continues. * * Return value: * 0 : timeout * >0 : signal triggered */ int osal_wait_for_signal_timeout(P_OSAL_SIGNAL pSignal, P_OSAL_THREAD pThread) { OSAL_THREAD_SCHEDSTATS schedstats; int waitRet; /* [ChangeFeature][George] gps driver may be closed by -ERESTARTSYS. * Avoid using *interruptible" version in order to complete our jobs, * such as function off gracefully. */ if (!pThread || !pThread->pThread) return wait_for_completion_timeout(&pSignal->comp, msecs_to_jiffies(pSignal->timeoutValue)); do { osal_thread_sched_mark(pThread, &schedstats); waitRet = wait_for_completion_timeout(&pSignal->comp, msecs_to_jiffies(pSignal->timeoutValue)); osal_thread_sched_unmark(pThread, &schedstats); if (waitRet > 0) break; if (schedstats.runnable > schedstats.exec) { pr_err( "[E]%s:wait completion timeout, %s cannot get CPU, extension(%d), show backtrace:\n", __func__, pThread->threadName, pSignal->timeoutExtension); } else { pr_err( "[E]%s:wait completion timeout, show %s backtrace:\n", __func__, pThread->threadName); pSignal->timeoutExtension = 0; } pr_err( "[E]%s:\tduration:%llums, sched(x%llu/r%llu/i%llu)\n", __func__, schedstats.time, schedstats.exec, schedstats.runnable, schedstats.iowait); /* * no need to disginguish combo or A/D die projects * osal_dump_thread_state will just return if target * thread does not exist */ osal_dump_thread_state("mtk_wmtd"); osal_dump_thread_state("mtk_wmtd_worker"); osal_dump_thread_state("btif_rxd"); osal_dump_thread_state("mtk_stp_psm"); osal_dump_thread_state("mtk_stp_btm"); osal_dump_thread_state("stp_sdio_tx_rx"); } while (pSignal->timeoutExtension--); return waitRet; } int osal_raise_signal(P_OSAL_SIGNAL pSignal) { if (pSignal) { complete(&pSignal->comp); return 0; } else return -1; } int osal_signal_active_state(P_OSAL_SIGNAL pSignal) { if (pSignal) return pSignal->timeoutValue; else return -1; } int osal_signal_deinit(P_OSAL_SIGNAL pSignal) { if (pSignal) { pSignal->timeoutValue = 0; return 0; } else return -1; } /* * OSAL layer Event Opeartion related APIs * initialization * wait for signal * wait for signal timerout * raise signal * destroy a signal * */ int osal_event_init(P_OSAL_EVENT pEvent) { if (pEvent) { init_waitqueue_head(&pEvent->waitQueue); return 0; } return -1; } int osal_trigger_event(P_OSAL_EVENT pEvent) { int ret = 0; if (pEvent) { wake_up_interruptible(&pEvent->waitQueue); return ret; } return -1; } int osal_wait_for_event(P_OSAL_EVENT pEvent, int (*condition)(void *), void *cond_pa) { if (pEvent) return wait_event_interruptible(pEvent->waitQueue, condition(cond_pa)); else return -1; } int osal_wait_for_event_timeout(P_OSAL_EVENT pEvent, int (*condition)(void *), void *cond_pa) { if (pEvent) return wait_event_interruptible_timeout(pEvent->waitQueue, condition(cond_pa), msecs_to_jiffies(pEvent->timeoutValue)); return -1; } int osal_event_deinit(P_OSAL_EVENT pEvent) { return 0; } long osal_wait_for_event_bit_set(P_OSAL_EVENT pEvent, unsigned long *pState, unsigned int bitOffset) { unsigned int ms = 0; if (pEvent) { ms = pEvent->timeoutValue; if (ms != 0) return wait_event_interruptible_timeout( pEvent->waitQueue, test_bit(bitOffset, pState), msecs_to_jiffies(ms)); else return wait_event_interruptible(pEvent->waitQueue, test_bit(bitOffset, pState)); } else return -1; } long osal_wait_for_event_bit_clr(P_OSAL_EVENT pEvent, unsigned long *pState, unsigned int bitOffset) { unsigned int ms = 0; if (pEvent) { ms = pEvent->timeoutValue; if (ms != 0) return wait_event_interruptible_timeout( pEvent->waitQueue, !test_bit(bitOffset, pState), msecs_to_jiffies(ms)); else return wait_event_interruptible(pEvent->waitQueue, !test_bit(bitOffset, pState)); } else return -1; } /* * bit test and set/clear operations APIs */ #if OS_BIT_OPS_SUPPORT #define osal_bit_op_lock(x) #define osal_bit_op_unlock(x) #else int osal_bit_op_lock(P_OSAL_UNSLEEPABLE_LOCK pLock) { return 0; } int osal_bit_op_unlock(P_OSAL_UNSLEEPABLE_LOCK pLock) { return 0; } #endif int osal_clear_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) { osal_bit_op_lock(&(pData->opLock)); clear_bit(bitOffset, &pData->data); osal_bit_op_unlock(&(pData->opLock)); return 0; } int osal_set_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) { osal_bit_op_lock(&(pData->opLock)); set_bit(bitOffset, &pData->data); osal_bit_op_unlock(&(pData->opLock)); return 0; } int osal_test_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) { unsigned int iRet = 0; osal_bit_op_lock(&(pData->opLock)); iRet = test_bit(bitOffset, &pData->data); osal_bit_op_unlock(&(pData->opLock)); return iRet; } int osal_test_and_clear_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) { unsigned int iRet = 0; osal_bit_op_lock(&(pData->opLock)); iRet = test_and_clear_bit(bitOffset, &pData->data); osal_bit_op_unlock(&(pData->opLock)); return iRet; } int osal_test_and_set_bit(unsigned int bitOffset, P_OSAL_BIT_OP_VAR pData) { unsigned int iRet = 0; osal_bit_op_lock(&(pData->opLock)); iRet = test_and_set_bit(bitOffset, &pData->data); osal_bit_op_unlock(&(pData->opLock)); return iRet; } /* * tiemr operations APIs * create * stop * modify * create * delete * */ int osal_timer_create(P_OSAL_TIMER pTimer) { struct timer_list *timer = &pTimer->timer; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)) timer_setup(timer, pTimer->timeoutHandler, 0); #else init_timer(timer); timer->function = pTimer->timeoutHandler; timer->data = (unsigned long)pTimer->timeroutHandlerData; #endif return 0; } int osal_timer_start(P_OSAL_TIMER pTimer, unsigned int ms) { struct timer_list *timer = &pTimer->timer; timer->expires = jiffies + (ms / (1000 / HZ)); add_timer(timer); return 0; } int osal_timer_stop(P_OSAL_TIMER pTimer) { struct timer_list *timer = &pTimer->timer; del_timer(timer); return 0; } int osal_timer_stop_sync(P_OSAL_TIMER pTimer) { struct timer_list *timer = &pTimer->timer; del_timer_sync(timer); return 0; } int osal_timer_modify(P_OSAL_TIMER pTimer, unsigned int ms) { mod_timer(&pTimer->timer, jiffies + (ms) / (1000 / HZ)); return 0; } int _osal_fifo_init(OSAL_FIFO *pFifo, unsigned char *buf, unsigned int size) { struct kfifo *fifo = NULL; int ret = -1; if (!pFifo) { pr_err("pFifo must be !NULL\n"); return -1; } if (pFifo->pFifoBody) { pr_err("pFifo->pFifoBody must be NULL\n"); pr_err("pFifo(0x%p), pFifo->pFifoBody(0x%p)\n", pFifo, pFifo->pFifoBody); return -1; } fifo = kzalloc(sizeof(struct kfifo), GFP_ATOMIC); if (!buf) { /*fifo's buffer is not ready, we allocate automatically */ ret = kfifo_alloc(fifo, size, /*GFP_KERNEL */ GFP_ATOMIC); } else { if (is_power_of_2(size)) { kfifo_init(fifo, buf, size); ret = 0; } else { kfifo_free(fifo); fifo = NULL; ret = -1; } } pFifo->pFifoBody = fifo; return (ret < 0) ? (-1) : (0); } int _osal_fifo_deinit(OSAL_FIFO *pFifo) { struct kfifo *fifo = NULL; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo) kfifo_free(fifo); return 0; } int _osal_fifo_size(OSAL_FIFO *pFifo) { struct kfifo *fifo = NULL; int ret = 0; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo) ret = kfifo_size(fifo); return ret; } /*returns unused bytes in fifo*/ int _osal_fifo_avail_size(OSAL_FIFO *pFifo) { struct kfifo *fifo = NULL; int ret = 0; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo) ret = kfifo_avail(fifo); return ret; } /*returns used bytes in fifo*/ int _osal_fifo_len(OSAL_FIFO *pFifo) { struct kfifo *fifo = NULL; int ret = 0; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo) ret = kfifo_len(fifo); return ret; } int _osal_fifo_is_empty(OSAL_FIFO *pFifo) { struct kfifo *fifo = NULL; int ret = 0; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo) ret = kfifo_is_empty(fifo); return ret; } int _osal_fifo_is_full(OSAL_FIFO *pFifo) { struct kfifo *fifo = NULL; int ret = 0; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo) ret = kfifo_is_full(fifo); return ret; } int _osal_fifo_data_in(OSAL_FIFO *pFifo, const void *buf, unsigned int len) { struct kfifo *fifo = NULL; int ret = 0; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo && buf && (len <= _osal_fifo_avail_size(pFifo))) { ret = kfifo_in(fifo, buf, len); } else { pr_err("%s: kfifo_in, error, len = %d, _osal_fifo_avail_size = %d, buf=%p\n", __func__, len, _osal_fifo_avail_size(pFifo), buf); ret = 0; } return ret; } int _osal_fifo_data_out(OSAL_FIFO *pFifo, void *buf, unsigned int len) { struct kfifo *fifo = NULL; int ret = 0; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n" , __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo && buf && (len <= _osal_fifo_len(pFifo))) { ret = kfifo_out(fifo, buf, len); } else { pr_err("%s: kfifo_out, error, len = %d, osal_fifo_len = %d, buf=%p\n", __func__, len, _osal_fifo_len(pFifo), buf); ret = 0; } return ret; } int _osal_fifo_reset(OSAL_FIFO *pFifo) { struct kfifo *fifo = NULL; if (!pFifo || !pFifo->pFifoBody) { pr_err("%s:pFifo = NULL or pFifo->pFifoBody = NULL, error\n", __func__); return -1; } fifo = (struct kfifo *)pFifo->pFifoBody; if (fifo) kfifo_reset(fifo); return 0; } int osal_fifo_init(P_OSAL_FIFO pFifo, unsigned char *buffer, unsigned int size) { if (!pFifo) { pr_err("%s:pFifo = NULL, error\n", __func__); return -1; } pFifo->FifoInit = _osal_fifo_init; pFifo->FifoDeInit = _osal_fifo_deinit; pFifo->FifoSz = _osal_fifo_size; pFifo->FifoAvailSz = _osal_fifo_avail_size; pFifo->FifoLen = _osal_fifo_len; pFifo->FifoIsEmpty = _osal_fifo_is_empty; pFifo->FifoIsFull = _osal_fifo_is_full; pFifo->FifoDataIn = _osal_fifo_data_in; pFifo->FifoDataOut = _osal_fifo_data_out; pFifo->FifoReset = _osal_fifo_reset; if (pFifo->pFifoBody != NULL) { pr_err("%s:Because pFifo room is avialable, we clear the room and allocate them again.\n", __func__); pFifo->FifoDeInit(pFifo->pFifoBody); pFifo->pFifoBody = NULL; } pFifo->FifoInit(pFifo, buffer, size); return 0; } void osal_fifo_deinit(P_OSAL_FIFO pFifo) { if (pFifo) pFifo->FifoDeInit(pFifo); else { pr_err("%s:pFifo = NULL, error\n", __func__); return; } kfree(pFifo->pFifoBody); } int osal_fifo_reset(P_OSAL_FIFO pFifo) { int ret = -1; if (pFifo) { ret = pFifo->FifoReset(pFifo); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = -1; } return ret; } unsigned int osal_fifo_in(P_OSAL_FIFO pFifo, unsigned char *buffer, unsigned int size) { unsigned int ret = 0; if (pFifo) { ret = pFifo->FifoDataIn(pFifo, buffer, size); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = 0; } return ret; } unsigned int osal_fifo_out(P_OSAL_FIFO pFifo, unsigned char *buffer, unsigned int size) { unsigned int ret = 0; if (pFifo) { ret = pFifo->FifoDataOut(pFifo, buffer, size); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = 0; } return ret; } unsigned int osal_fifo_len(P_OSAL_FIFO pFifo) { unsigned int ret = 0; if (pFifo) { ret = pFifo->FifoLen(pFifo); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = 0; } return ret; } unsigned int osal_fifo_sz(P_OSAL_FIFO pFifo) { unsigned int ret = 0; if (pFifo) { ret = pFifo->FifoSz(pFifo); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = 0; } return ret; } unsigned int osal_fifo_avail(P_OSAL_FIFO pFifo) { unsigned int ret = 0; if (pFifo) { ret = pFifo->FifoAvailSz(pFifo); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = 0; } return ret; } unsigned int osal_fifo_is_empty(P_OSAL_FIFO pFifo) { unsigned int ret = 0; if (pFifo) { ret = pFifo->FifoIsEmpty(pFifo); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = 0; } return ret; } unsigned int osal_fifo_is_full(P_OSAL_FIFO pFifo) { unsigned int ret = 0; if (pFifo) { ret = pFifo->FifoIsFull(pFifo); } else { pr_err("%s:pFifo = NULL, error\n", __func__); ret = 0; } return ret; } /* * sleepable lock operations APIs * init * lock * unlock * destroy * */ #if !defined(CONFIG_PROVE_LOCKING) int osal_unsleepable_lock_init(P_OSAL_UNSLEEPABLE_LOCK pUSL) { spin_lock_init(&(pUSL->lock)); return 0; } #endif int osal_lock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL) { spin_lock_irqsave(&(pUSL->lock), pUSL->flag); return 0; } int osal_unlock_unsleepable_lock(P_OSAL_UNSLEEPABLE_LOCK pUSL) { spin_unlock_irqrestore(&(pUSL->lock), pUSL->flag); return 0; } int osal_unsleepable_lock_deinit(P_OSAL_UNSLEEPABLE_LOCK pUSL) { return 0; } /* * unsleepable operations APIs * init * lock * unlock * destroy * */ #if !defined(CONFIG_PROVE_LOCKING) int osal_sleepable_lock_init(P_OSAL_SLEEPABLE_LOCK pSL) { mutex_init(&pSL->lock); return 0; } #endif int osal_lock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK pSL) { return mutex_lock_killable(&pSL->lock); } int osal_unlock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK pSL) { mutex_unlock(&pSL->lock); return 0; } int osal_trylock_sleepable_lock(P_OSAL_SLEEPABLE_LOCK pSL) { return mutex_trylock(&pSL->lock); } int osal_sleepable_lock_deinit(P_OSAL_SLEEPABLE_LOCK pSL) { mutex_destroy(&pSL->lock); return 0; } int osal_sleep_ms(unsigned int ms) { msleep(ms); return 0; } int osal_udelay(unsigned int us) { udelay(us); return 0; } int osal_usleep_range(unsigned long min, unsigned long max) { usleep_range(min, max); return 0; } int osal_gettimeofday(struct timespec64 *tv) { if (tv == NULL) return -1; ktime_get_real_ts64(tv); return 0; } void osal_get_local_time(unsigned long long *sec, unsigned long *nsec) { if (sec != NULL && nsec != NULL) { *sec = local_clock(); *nsec = do_div(*sec, 1000000000)/1000; } else pr_err("The input parameters error when get local time\n"); } unsigned long long osal_elapsed_us(unsigned long long ts, unsigned long usec) { unsigned long long current_ts = 0; unsigned long current_usec = 0; osal_get_local_time(¤t_ts, ¤t_usec); return (current_ts*1000000 + current_usec) - (ts*1000000 + usec); } void osal_buffer_dump(const unsigned char *buf, const unsigned char *title, const unsigned int len, const unsigned int limit) { int k; unsigned int dump_len; char str[DBG_LOG_STR_SIZE] = {""}; int strlen = 0; pr_info("[%s] len=%d, limit=%d, start dump\n", title, len, limit); dump_len = ((limit != 0) && (len > limit)) ? limit : len; for (k = 0; k < dump_len; k++) { if ((k+1) % 16 != 0) { strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, "%02x ", buf[k]); } else { strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, "%02x ", buf[k]); pr_info("%s", str); strlen = 0; } } if (k % 16 != 0) pr_info("%s\n", str); pr_info("end of dump\n"); } void osal_buffer_dump_data(const unsigned int *buf, const unsigned char *title, const unsigned int len, const unsigned int limit, const int flag) { int k; unsigned int dump_len; char str[DBG_LOG_STR_SIZE] = {""}; int strlen = 0; dump_len = ((limit != 0) && (len > limit)) ? limit : len; for (k = 0; k < dump_len; k++) { if (((k+1) % 8 != 0) && (k < (dump_len - 1))) { strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, "0x%08x,", buf[k]); } else { strlen += osal_snprintf(str + strlen, DBG_LOG_STR_SIZE - strlen, "0x%08x,", buf[k]); if (flag) osal_ftrace_print("%s%s", title, str); else pr_info("%s%s", title, str); strlen = 0; } } if (k % 8 != 0) { if (flag) osal_ftrace_print("%s%s", title, str); else pr_info("%s%s", title, str); } } unsigned int osal_op_get_id(P_OSAL_OP pOp) { return (pOp) ? pOp->op.opId : 0xFFFFFFFF; } MTK_CONN_BOOL osal_op_is_wait_for_signal(P_OSAL_OP pOp) { return (pOp && pOp->signal.timeoutValue) ? MTK_CONN_BOOL_TRUE : MTK_CONN_BOOL_FALSE; } void osal_op_raise_signal(P_OSAL_OP pOp, int result) { if (pOp) { pOp->result = result; osal_raise_signal(&pOp->signal); } } int osal_ftrace_print(const char *str, ...) { int ret = 0; #ifdef CONFIG_TRACING va_list args; char tempString[DBG_LOG_STR_SIZE]; if (ftrace_flag) { va_start(args, str); ret = vsnprintf(tempString, DBG_LOG_STR_SIZE, str, args); va_end(args); if (ret < 0) pr_notice("%s ret = %d failed\n", __func__, ret); else trace_printk("%s\n", tempString); } #endif return ret; } int osal_ftrace_print_ctrl(int flag) { #ifdef CONFIG_TRACING if (flag) ftrace_flag = 1; else ftrace_flag = 0; #endif return 0; } void osal_set_op_result(P_OSAL_OP pOp, int result) { if (pOp) pOp->result = result; } MTK_CONN_BOOL osal_opq_has_op(P_OSAL_OP_Q pOpQ, P_OSAL_OP pOp) { unsigned int rd; unsigned int wt; P_OSAL_OP op; rd = pOpQ->read; wt = pOpQ->write; while (rd != wt) { op = pOpQ->queue[rd & RB_MASK(pOpQ)]; if (op == pOp) return MTK_CONN_BOOL_TRUE; rd++; } return MTK_CONN_BOOL_FALSE; } static void osal_op_history_print_work(struct work_struct *work) { struct osal_op_history *log_history = container_of(work, struct osal_op_history, dump_work); struct ring *ring_buffer = &log_history->dump_ring_buffer; struct ring_segment seg; struct osal_op_history_entry *queue = ring_buffer->base; struct osal_op_history_entry *entry; int index = 0; if (queue == NULL) { pr_info("queue shouldn't be NULL, %s", log_history->name); return; } RING_READ_FOR_EACH_ITEM(RING_SIZE(ring_buffer), seg, ring_buffer) { index = seg.ring_pt - ring_buffer->base; entry = &queue[index]; pr_info("(%llu.%06lu) %s: pOp(%p):%u(%d)-%x-%zx,%zx,%zx,%zx\n", entry->ts, entry->usec, log_history->name, entry->opbuf_address, entry->op_id, entry->opbuf_ref_count, entry->op_info_bit, entry->param_0, entry->param_1, entry->param_2, entry->param_3); } kfree(queue); ring_buffer->base = NULL; } void osal_op_history_init(struct osal_op_history *log_history, int queue_size) { int size = queue_size * sizeof(struct osal_op_history_entry); spin_lock_init(&(log_history->lock)); log_history->queue = kzalloc(size, GFP_ATOMIC); if (log_history->queue == NULL) return; /* queue_size must be power of 2 */ ring_init( &log_history->queue, queue_size, 0, 0, &log_history->ring_buffer); INIT_WORK(&log_history->dump_work, osal_op_history_print_work); } void osal_op_history_print(struct osal_op_history *log_history, char *name) { struct osal_op_history_entry *queue; struct ring *ring_buffer, *dump_ring_buffer; int queue_size; unsigned long flags; struct work_struct *work = &log_history->dump_work; spinlock_t *lock = &(log_history->lock); if (log_history->queue == NULL) { pr_info("Queue is NULL, name: %s\n", name); return; } ring_buffer = &log_history->ring_buffer; queue_size = sizeof(struct osal_op_history_entry) * RING_SIZE(ring_buffer); /* Allocate memory before getting lock to save time of holding lock */ queue = kmalloc(queue_size, GFP_KERNEL); if (queue == NULL) return; dump_ring_buffer = &log_history->dump_ring_buffer; spin_lock_irqsave(lock, flags); if (dump_ring_buffer->base != NULL) { spin_unlock_irqrestore(lock, flags); kfree(queue); pr_info("print is ongoing: %s\n", name); return; } osal_snprintf(log_history->name, sizeof(log_history->name), "%s", name); osal_memcpy(queue, log_history->queue, queue_size); osal_memcpy(dump_ring_buffer, ring_buffer, sizeof(struct ring)); /* assign value to base after memory copy */ dump_ring_buffer->base = queue; spin_unlock_irqrestore(lock, flags); schedule_work(work); } void osal_op_history_save(struct osal_op_history *log_history, P_OSAL_OP pOp) { struct osal_op_history_entry *entry = NULL; struct ring_segment seg; int index; unsigned long long sec = 0; unsigned long usec = 0; unsigned long flags; if (log_history->queue == NULL) return; osal_get_local_time(&sec, &usec); spin_lock_irqsave(&(log_history->lock), flags); RING_OVERWRITE_FOR_EACH(1, seg, &log_history->ring_buffer) { index = seg.ring_pt - log_history->ring_buffer.base; entry = &log_history->queue[index]; } if (entry == NULL) { pr_info("Entry is null, size %d\n", RING_SIZE(&log_history->ring_buffer)); spin_unlock_irqrestore(&(log_history->lock), flags); return; } entry->opbuf_address = pOp; entry->op_id = pOp->op.opId; entry->opbuf_ref_count = atomic_read(&pOp->ref_count); entry->op_info_bit = pOp->op.u4InfoBit; entry->param_0 = pOp->op.au4OpData[0]; entry->param_1 = pOp->op.au4OpData[1]; entry->param_2 = pOp->op.au4OpData[2]; entry->param_3 = pOp->op.au4OpData[3]; entry->ts = sec; entry->usec = usec; spin_unlock_irqrestore(&(log_history->lock), flags); } static inline void osal_systrace_prepare(void) { /* Since K419, the function is not exported. */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)) if (unlikely(mark_addr == 0)) mark_addr = kallsyms_lookup_name("tracing_mark_write"); #endif if (unlikely(g_pid == 0)) g_pid = task_pid_nr(current); } static void osal_systrace_b(const char *log) { osal_systrace_prepare(); preempt_disable(); KERNEL_event_trace_printk(mark_addr, "B|%d|%s\n", g_pid, log); preempt_enable(); } static void osal_systrace_e(void) { preempt_disable(); KERNEL_event_trace_printk(mark_addr, "E\n"); preempt_enable(); } static void osal_systrace_c(int val, const char *log) { osal_systrace_prepare(); preempt_disable(); KERNEL_event_trace_printk(mark_addr, "C|%d|%s|%d\n", g_pid, log, val); preempt_enable(); } void osal_systrace_major_b(const char *fmt, ...) { char log[DBG_LOG_STR_SIZE]; va_list args; memset(log, ' ', sizeof(log)); va_start(args, fmt); if (vsnprintf(log, sizeof(log), fmt, args) < 0) pr_err("[%s:%d] vsnprintf error", __func__, __LINE__); va_end(args); osal_systrace_b(log); } void osal_systrace_major_e(void) { osal_systrace_e(); } void osal_systrace_minor_b(const char *fmt, ...) { char log[DBG_LOG_STR_SIZE]; va_list args; if (!ftrace_flag) return; memset(log, ' ', sizeof(log)); va_start(args, fmt); if (vsnprintf(log, sizeof(log), fmt, args) < 0) pr_err("[%s:%d] vsnprintf error", __func__, __LINE__); va_end(args); osal_systrace_b(log); } void osal_systrace_minor_e(void) { if (!ftrace_flag) return; osal_systrace_e(); } void osal_systrace_minor_c(int val, const char *fmt, ...) { char log[DBG_LOG_STR_SIZE]; va_list args; if (!ftrace_flag) return; memset(log, ' ', sizeof(log)); va_start(args, fmt); if (vsnprintf(log, sizeof(log), fmt, args) < 0) pr_err("[%s:%d] vsnprintf error", __func__, __LINE__); va_end(args); osal_systrace_c(val, log); } int osal_wake_lock_init(struct osal_wake_lock *pLock) { if (!pLock) return -1; if (pLock->init_flag == 0) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 149)) pLock->wake_lock = wakeup_source_register(NULL, pLock->name); #else pLock->wake_lock = wakeup_source_register(pLock->name); #endif pLock->init_flag = 1; } return 0; } int osal_wake_lock_deinit(struct osal_wake_lock *pLock) { if (!pLock) return -1; if (pLock->init_flag == 1) { wakeup_source_unregister(pLock->wake_lock); pLock->init_flag = 0; } else pr_info("%s: wake_lock is not initialized!\n", __func__); return 0; } int osal_wake_lock(struct osal_wake_lock *pLock) { if (!pLock) return -1; if (pLock->init_flag == 1) __pm_stay_awake(pLock->wake_lock); else pr_info("%s: wake_lock is not initialized!\n", __func__); return 0; } int osal_wake_unlock(struct osal_wake_lock *pLock) { if (!pLock) return -1; if (pLock->init_flag == 1) __pm_relax(pLock->wake_lock); else pr_info("%s: wake_lock is not initialized!\n", __func__); return 0; } int osal_wake_lock_count(struct osal_wake_lock *pLock) { int count = 0; if (!pLock) return -1; if (pLock->init_flag == 1) count = pLock->wake_lock->active; else pr_info("%s: wake_lock is not initialized!\n", __func__); return count; }