/** @file moal_shim.c * * @brief This file contains the callback functions registered to MLAN * * Copyright (C) 2008-2019, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. * */ /******************************************************** Change log: 10/21/2008: initial version ********************************************************/ #include "moal_main.h" #include "moal_pcie.h" #ifdef UAP_SUPPORT #include "moal_uap.h" #endif #if defined(STA_CFG80211) || defined(UAP_CFG80211) #include "moal_cfg80211.h" #include "moal_cfgvendor.h" #endif extern int drv_mode; #include /******************************************************** Local Variables ********************************************************/ /** moal_lock */ typedef struct _moal_lock { /** Lock */ spinlock_t lock; /** Flags */ unsigned long flags; } moal_lock; /******************************************************** Global Variables ********************************************************/ extern int cfg80211_wext; extern int hw_test; #ifdef ANDROID_KERNEL extern int wakelock_timeout; #endif #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) #if defined(STA_CFG80211) || defined(UAP_CFG80211) extern int dfs_offload; #endif #endif /** napi support*/ extern int napi; typedef MLAN_PACK_START struct { t_u32 t4; t_u8 t4_error; t_u32 t1; t_u8 t1_error; t_u64 egress_time; } MLAN_PACK_END confirm_timestamps; /******************************************************** Local Functions ********************************************************/ /******************************************************** Global Functions ********************************************************/ /** * @brief Alloc a buffer * * @param pmoal_handle Pointer to the MOAL context * @param size The size of the buffer to be allocated * @param flag The type of the buffer to be allocated * @param ppbuf Pointer to a buffer location to store buffer pointer allocated * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_malloc(IN t_void *pmoal_handle, IN t_u32 size, IN t_u32 flag, OUT t_u8 **ppbuf) { moal_handle *handle = (moal_handle *)pmoal_handle; t_u32 mem_flag = (in_interrupt() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; if (flag & MLAN_MEM_DMA) mem_flag |= GFP_DMA; *ppbuf = kzalloc(size, mem_flag); if (*ppbuf == NULL) { PRINTM(MERROR, "%s: allocate memory (%d bytes) failed!\n", __func__, (int)size); return MLAN_STATUS_FAILURE; } atomic_inc(&handle->malloc_count); return MLAN_STATUS_SUCCESS; } /** * @brief Free a buffer * * @param pmoal_handle Pointer to the MOAL context * @param pbuf Pointer to the buffer to be freed * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_mfree(IN t_void *pmoal_handle, IN t_u8 *pbuf) { moal_handle *handle = (moal_handle *)pmoal_handle; if (!pbuf) return MLAN_STATUS_FAILURE; kfree(pbuf); atomic_dec(&handle->malloc_count); return MLAN_STATUS_SUCCESS; } /** * @brief Alloc a vitual-address-continuous buffer * * @param pmoal_handle Pointer to the MOAL context * @param size The size of the buffer to be allocated * @param ppbuf Pointer to a buffer location to store buffer pointer allocated * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_vmalloc(IN t_void *pmoal_handle, IN t_u32 size, OUT t_u8 **ppbuf) { moal_handle *handle = (moal_handle *)pmoal_handle; *ppbuf = vmalloc(size); if (*ppbuf == NULL) { PRINTM(MERROR, "%s: vmalloc (%d bytes) failed!", __func__, (int)size); return MLAN_STATUS_FAILURE; } atomic_inc(&handle->vmalloc_count); return MLAN_STATUS_SUCCESS; } /** * @brief Free a buffer allocated by vmalloc * * @param pmoal_handle Pointer to the MOAL context * @param pbuf Pointer to the buffer to be freed * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_vfree(IN t_void *pmoal_handle, IN t_u8 *pbuf) { moal_handle *handle = (moal_handle *)pmoal_handle; if (!pbuf) return MLAN_STATUS_FAILURE; vfree(pbuf); atomic_dec(&handle->vmalloc_count); return MLAN_STATUS_SUCCESS; } /** * @brief Alloc a consistent block of memory * * @param pmoal_handle Pointer to the MOAL context * @param size The size of the buffer to be allocated * @param ppbuf Pointer to a buffer location to store memory allocated * @param pbuf_pa Pointer to a buffer location to store physical address of above memory * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_malloc_consistent(IN t_void *pmoal_handle, IN t_u32 size, OUT t_u8 **ppbuf, OUT t_u64 *pbuf_pa) { moal_handle *handle = (moal_handle *)pmoal_handle; pcie_service_card *card = (pcie_service_card *)handle->card; *pbuf_pa = 0; *ppbuf = (t_u8 *)pci_alloc_consistent(card->dev, size, (dma_addr_t *) pbuf_pa); if (*ppbuf == NULL) { PRINTM(MERROR, "%s: allocate consistent memory (%d bytes) failed!\n", __func__, (int)size); return MLAN_STATUS_FAILURE; } atomic_inc(&handle->malloc_cons_count); return MLAN_STATUS_SUCCESS; } /** * @brief Free a consistent block of memory * * @param pmoal_handle Pointer to the MOAL context * @param size Size of them memory to be freed * @param pbuf Pointer to the memory to be freed * @param buf_pa Physical address of the memory to be freed * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_mfree_consistent(IN t_void *pmoal_handle, IN t_u32 size, IN t_u8 *pbuf, IN t_u64 buf_pa) { moal_handle *handle = (moal_handle *)pmoal_handle; pcie_service_card *card = handle->card; if (!pbuf) return MLAN_STATUS_FAILURE; pci_free_consistent(card->dev, size, pbuf, buf_pa); atomic_dec(&handle->malloc_cons_count); return MLAN_STATUS_SUCCESS; } /** * @brief Map a block of memory to device * * @param pmoal_handle Pointer to the MOAL context * @param pbuf Pointer to the buffer to be mapped * @param pbuf_pa Pointer to store the physical address of buffer * @param size Size of the buffer to be mapped * @param flag Flags for mapping IO * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_map_memory(IN t_void *pmoal_handle, IN t_u8 *pbuf, OUT t_u64 *pbuf_pa, IN t_u32 size, IN t_u32 flag) { moal_handle *handle = (moal_handle *)pmoal_handle; pcie_service_card *card = (pcie_service_card *)handle->card; dma_addr_t dma; /* Init memory to device */ dma = pci_map_single(card->dev, pbuf, size, flag); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) if (pci_dma_mapping_error(card->dev, dma)) { #else if (pci_dma_mapping_error(dma)) { #endif PRINTM(MERROR, "Tx ring: failed to pci_map_single\n"); return MLAN_STATUS_FAILURE; } *pbuf_pa = dma; return MLAN_STATUS_SUCCESS; } /** * @brief Unmap a block of memory from device * * @param pmoal_handle Pointer to the MOAL context * @param pbuf Pointer to the buffer to unmap * @param buf_pa Physical address of buffer to unmap * @param size Size of the buffer to unmap * @param flag Flags for mapping IO * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_unmap_memory(IN t_void *pmoal_handle, IN t_u8 *pbuf, IN t_u64 buf_pa, IN t_u32 size, IN t_u32 flag) { moal_handle *handle = (moal_handle *)pmoal_handle; pcie_service_card *card = (pcie_service_card *)handle->card; pci_unmap_single(card->dev, buf_pa, size, flag); return MLAN_STATUS_SUCCESS; } /** * @brief Fill memory with constant byte * * @param pmoal_handle Pointer to the MOAL context * @param pmem Pointer to the memory area * @param byte A constant byte * @param num Number of bytes to fill * * @return Pointer to the memory area */ t_void * moal_memset(IN t_void *pmoal_handle, IN t_void *pmem, IN t_u8 byte, IN t_u32 num) { t_void *p = pmem; if (pmem && num) p = memset(pmem, byte, num); return p; } /** * @brief Copy memory from one area to another * * @param pmoal_handle Pointer to the MOAL context * @param pdest Pointer to the dest memory * @param psrc Pointer to the src memory * @param num Number of bytes to move * * @return Pointer to the dest memory */ t_void * moal_memcpy(IN t_void *pmoal_handle, IN t_void *pdest, IN const t_void *psrc, IN t_u32 num) { t_void *p = pdest; if (pdest && psrc && num) p = memcpy(pdest, psrc, num); return p; } /** * @brief Move memory from one area to another * * @param pmoal_handle Pointer to the MOAL context * @param pdest Pointer to the dest memory * @param psrc Pointer to the src memory * @param num Number of bytes to move * * @return Pointer to the dest memory */ t_void * moal_memmove(IN t_void *pmoal_handle, IN t_void *pdest, IN const t_void *psrc, IN t_u32 num) { t_void *p = pdest; if (pdest && psrc && num) p = memmove(pdest, psrc, num); return p; } /** * @brief Compare two memory areas * * @param pmoal_handle Pointer to the MOAL context * @param pmem1 Pointer to the first memory * @param pmem2 Pointer to the second memory * @param num Number of bytes to compare * * @return Compare result returns by memcmp */ t_s32 moal_memcmp(IN t_void *pmoal_handle, IN const t_void *pmem1, IN const t_void *pmem2, IN t_u32 num) { t_s32 result; result = memcmp(pmem1, pmem2, num); return result; } /** * @brief Delay function * * @param pmoal_handle Pointer to the MOAL context * @param delay delay in micro-second * * @return N/A */ t_void moal_udelay(IN t_void *pmoal_handle, IN t_u32 delay) { if (delay >= 1000) mdelay(delay / 1000); if (delay % 1000) udelay(delay % 1000); } /** * @brief Retrieves the current system time * * @param pmoal_handle Pointer to the MOAL context * @param psec Pointer to buf for the seconds of system time * @param pusec Pointer to buf the micro seconds of system time * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_get_system_time(IN t_void *pmoal_handle, OUT t_u32 *psec, OUT t_u32 *pusec) { struct timeval t; woal_get_monotonic_time(&t); *psec = (t_u32)t.tv_sec; *pusec = (t_u32)t.tv_usec; return MLAN_STATUS_SUCCESS; } /** * @brief usleep function * * @param pmoal_handle Pointer to the MOAL context * @param usmin Minimum value for sleep in usecs * @param usmax Maximum value for sleep in usecs * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_usleep(IN t_void *pmoal_handle, IN t_u64 min, IN t_u64 max) { usleep_range(min, max); return MLAN_STATUS_SUCCESS; } /** * @brief Retrieves the current boot time * * @param pmoal_handle Pointer to the MOAL context * @param pnsec Pointer to buf for the Nanoseconds of boot time * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_get_boot_ktime(IN t_void *pmoal_handle, OUT t_u64 *pnsec) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) ktime_t time; time = ktime_get_with_offset(TK_OFFS_BOOT); *pnsec = *(t_u64 *)&(time); #endif return MLAN_STATUS_SUCCESS; } /** * @brief Initializes the timer * * @param pmoal_handle Pointer to the MOAL context * @param pptimer Pointer to the timer * @param callback Pointer to callback function * @param pcontext Pointer to context * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_init_timer(IN t_void *pmoal_handle, OUT t_void **pptimer, IN t_void (*callback) (t_void *pcontext), IN t_void *pcontext) { moal_drv_timer *timer = NULL; t_u32 mem_flag = (in_interrupt() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; timer = kmalloc(sizeof(moal_drv_timer), mem_flag); if (timer == NULL) return MLAN_STATUS_FAILURE; woal_initialize_timer(timer, callback, pcontext); *pptimer = (t_void *)timer; return MLAN_STATUS_SUCCESS; } /** * @brief Free the timer * * @param pmoal_handle Pointer to the MOAL context * @param ptimer Pointer to the timer * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_free_timer(IN t_void *pmoal_handle, IN t_void *ptimer) { moal_drv_timer *timer = (moal_drv_timer *)ptimer; if (timer) { if ((timer->timer_is_canceled == MFALSE) && timer->time_period) { PRINTM(MWARN, "mlan try to free timer without stop timer!\n"); woal_cancel_timer(timer); } kfree(timer); } return MLAN_STATUS_SUCCESS; } /** * @brief Start the timer * * @param pmoal_handle Pointer to the MOAL context * @param ptimer Pointer to the timer * @param periodic Periodic timer * @param msec Timer value in milliseconds * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_start_timer(IN t_void *pmoal_handle, IN t_void *ptimer, IN t_u8 periodic, IN t_u32 msec) { if (!ptimer) return MLAN_STATUS_FAILURE; ((moal_drv_timer *)ptimer)->timer_is_periodic = periodic; woal_mod_timer((moal_drv_timer *)ptimer, msec); return MLAN_STATUS_SUCCESS; } /** * @brief Stop the timer * * @param pmoal_handle Pointer to the MOAL context * @param ptimer Pointer to the timer * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_stop_timer(IN t_void *pmoal_handle, IN t_void *ptimer) { if (!ptimer) return MLAN_STATUS_FAILURE; woal_cancel_timer((moal_drv_timer *)ptimer); return MLAN_STATUS_SUCCESS; } /** * @brief Initializes the lock * * @param pmoal_handle Pointer to the MOAL context * @param pplock Pointer to the lock * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_init_lock(IN t_void *pmoal_handle, OUT t_void **pplock) { moal_handle *handle = (moal_handle *)pmoal_handle; moal_lock *mlock = NULL; mlock = kmalloc(sizeof(moal_lock), GFP_ATOMIC); if (!mlock) return MLAN_STATUS_FAILURE; spin_lock_init(&mlock->lock); *pplock = (t_void *)mlock; atomic_inc(&handle->lock_count); return MLAN_STATUS_SUCCESS; } /** * @brief Free the lock * * @param pmoal_handle Pointer to the MOAL context * @param plock Lock * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_free_lock(IN t_void *pmoal_handle, IN t_void *plock) { moal_handle *handle = (moal_handle *)pmoal_handle; moal_lock *mlock = plock; kfree(mlock); if (mlock) atomic_dec(&handle->lock_count); return MLAN_STATUS_SUCCESS; } /** * @brief Request a spin lock * * @param pmoal_handle Pointer to the MOAL context * @param plock Pointer to the lock * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_spin_lock(IN t_void *pmoal_handle, IN t_void *plock) { moal_lock *mlock = plock; unsigned long flags = 0; if (mlock) { spin_lock_irqsave(&mlock->lock, flags); mlock->flags = flags; return MLAN_STATUS_SUCCESS; } else { return MLAN_STATUS_FAILURE; } } /** * @brief Request a spin_unlock * * @param pmoal_handle Pointer to the MOAL context * @param plock Pointer to the lock * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_spin_unlock(IN t_void *pmoal_handle, IN t_void *plock) { moal_lock *mlock = (moal_lock *)plock; if (mlock) { spin_unlock_irqrestore(&mlock->lock, mlock->flags); return MLAN_STATUS_SUCCESS; } else { return MLAN_STATUS_FAILURE; } } /** * @brief This function reads one block of firmware data from MOAL * * @param pmoal_handle Pointer to the MOAL context * @param offset Offset from where the data will be copied * @param len Length to be copied * @param pbuf Buffer where the data will be copied * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_get_fw_data(IN t_void *pmoal_handle, IN t_u32 offset, IN t_u32 len, OUT t_u8 *pbuf) { moal_handle *handle = (moal_handle *)pmoal_handle; if (!pbuf || !len) return MLAN_STATUS_FAILURE; if (offset + len > handle->firmware->size) return MLAN_STATUS_FAILURE; memcpy(pbuf, handle->firmware->data + offset, len); return MLAN_STATUS_SUCCESS; } /** * @brief This function is called when MLAN completes the initialization firmware. * * @param pmoal_handle Pointer to the MOAL context * @param status The status code for mlan_init_fw request * @param phw pointer to mlan_hw_info * @param ptbl pointer to mplan_bss_tbl * @return MLAN_STATUS_SUCCESS */ mlan_status moal_get_hw_spec_complete(IN t_void *pmoal_handle, IN mlan_status status, IN mlan_hw_info * phw, IN pmlan_bss_tbl ptbl) { ENTER(); if (status == MLAN_STATUS_SUCCESS) { PRINTM(MCMND, "Get Hw Spec done, fw_cap=0x%x\n", phw->fw_cap); } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function is called when MLAN completes the initialization firmware. * * @param pmoal_handle Pointer to the MOAL context * @param status The status code for mlan_init_fw request * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_init_fw_complete(IN t_void *pmoal_handle, IN mlan_status status) { moal_handle *handle = (moal_handle *)pmoal_handle; ENTER(); if (status == MLAN_STATUS_SUCCESS) handle->hardware_status = HardwareStatusReady; handle->init_wait_q_woken = MTRUE; wake_up(&handle->init_wait_q); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function is called when MLAN shutdown firmware is completed. * * @param pmoal_handle Pointer to the MOAL context * @param status The status code for mlan_shutdown request * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_shutdown_fw_complete(IN t_void *pmoal_handle, IN mlan_status status) { moal_handle *handle = (moal_handle *)pmoal_handle; ENTER(); handle->hardware_status = HardwareStatusNotReady; handle->init_wait_q_woken = MTRUE; wake_up_interruptible(&handle->init_wait_q); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function is called when an MLAN IOCTL is completed. * * @param pmoal_handle Pointer to the MOAL context * @param pioctl_req pointer to structure mlan_ioctl_req * @param status The status code for mlan_ioctl request * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_ioctl_complete(IN t_void *pmoal_handle, IN pmlan_ioctl_req pioctl_req, IN mlan_status status) { moal_handle *handle = (moal_handle *)pmoal_handle; moal_private *priv = NULL; wait_queue *wait; unsigned long flags = 0; ENTER(); if (!atomic_read(&handle->ioctl_pending)) PRINTM(MERROR, "ERR: Unexpected IOCTL completed: %p\n", pioctl_req); else atomic_dec(&handle->ioctl_pending); priv = woal_bss_index_to_priv(handle, pioctl_req->bss_index); if (!priv) { PRINTM(MERROR, "IOCTL %p complete with NULL priv, bss_index=%d\n", pioctl_req, pioctl_req->bss_index); LEAVE(); return MLAN_STATUS_SUCCESS; } if (status != MLAN_STATUS_SUCCESS && status != MLAN_STATUS_COMPLETE) PRINTM(MERROR, "IOCTL failed: %p id=0x%x, sub_id=0x%x action=%d, status_code=0x%x\n", pioctl_req, pioctl_req->req_id, (*(t_u32 *)pioctl_req->pbuf), (int)pioctl_req->action, pioctl_req->status_code); else PRINTM(MIOCTL, "IOCTL completed: %p id=0x%x sub_id=0x%x, action=%d, status=%d, status_code=0x%x\n", pioctl_req, pioctl_req->req_id, (*(t_u32 *)pioctl_req->pbuf), (int)pioctl_req->action, status, pioctl_req->status_code); spin_lock_irqsave(&handle->driver_lock, flags); wait = (wait_queue *)pioctl_req->reserved_1; if (wait) { wait->condition = MTRUE; wait->status = status; if (wait->wait_timeout) { wake_up(&wait->wait); } else { if ((status != MLAN_STATUS_SUCCESS) && (pioctl_req->status_code == MLAN_ERROR_CMD_TIMEOUT)) { PRINTM(MERROR, "IOCTL: command timeout\n"); } else { wake_up_interruptible(&wait->wait); } } spin_unlock_irqrestore(&handle->driver_lock, flags); } else { spin_unlock_irqrestore(&handle->driver_lock, flags); if ((status == MLAN_STATUS_SUCCESS) && (pioctl_req->action == MLAN_ACT_GET)) woal_process_ioctl_resp(priv, pioctl_req); kfree(pioctl_req); } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function allocates mlan_buffer. * * @param pmoal_handle Pointer to the MOAL context * @param size allocation size requested * @param pmbuf pointer to pointer to the allocated buffer * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_alloc_mlan_buffer(IN t_void *pmoal_handle, IN t_u32 size, OUT pmlan_buffer *pmbuf) { *pmbuf = woal_alloc_mlan_buffer((moal_handle *)pmoal_handle, size); if (NULL == *pmbuf) return MLAN_STATUS_FAILURE; return MLAN_STATUS_SUCCESS; } /** * @brief This function frees mlan_buffer. * * @param pmoal_handle Pointer to the MOAL context * @param pmbuf pointer to buffer to be freed * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_free_mlan_buffer(IN t_void *pmoal_handle, IN pmlan_buffer pmbuf) { if (!pmbuf) return MLAN_STATUS_FAILURE; woal_free_mlan_buffer((moal_handle *)pmoal_handle, pmbuf); return MLAN_STATUS_SUCCESS; } /** * @brief This function is called when MLAN complete send data packet. * * @param pmoal_handle Pointer to the MOAL context * @param pmbuf Pointer to the mlan buffer structure * @param status The status code for mlan_send_packet request * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_send_packet_complete(IN t_void *pmoal_handle, IN pmlan_buffer pmbuf, IN mlan_status status) { moal_private *priv = NULL; moal_handle *handle = (moal_handle *)pmoal_handle; struct sk_buff *skb = NULL; #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) t_u32 index = 0; #endif ENTER(); if (pmbuf && pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { woal_free_mlan_buffer(handle, pmbuf); atomic_dec(&handle->tx_pending); goto done; } if (pmbuf) { priv = woal_bss_index_to_priv(pmoal_handle, pmbuf->bss_index); skb = (struct sk_buff *)pmbuf->pdesc; if (priv) { woal_set_trans_start(priv->netdev); if (skb) { if (status == MLAN_STATUS_SUCCESS) { priv->stats.tx_packets++; priv->stats.tx_bytes += skb->len; } else { priv->stats.tx_errors++; } #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) index = skb_get_queue_mapping(skb); atomic_dec(&handle->tx_pending); if (atomic_dec_return (&priv->wmm_tx_pending[index]) == LOW_TX_PENDING) { struct netdev_queue *txq = netdev_get_tx_queue(priv-> netdev, index); if (netif_tx_queue_stopped(txq)) { netif_tx_wake_queue(txq); PRINTM(MINFO, "Wakeup Kernel Queue:%d\n", index); } } #else /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */ if (atomic_dec_return(&handle->tx_pending) < LOW_TX_PENDING) { int i; for (i = 0; i < handle->priv_num; i++) { #ifdef STA_SUPPORT if ((GET_BSS_ROLE (handle->priv[i]) == MLAN_BSS_ROLE_STA) && (handle->priv[i]-> media_connected || priv-> is_adhoc_link_sensed)) { woal_wake_queue(handle-> priv [i]-> netdev); } #endif #ifdef UAP_SUPPORT if ((GET_BSS_ROLE (handle->priv[i]) == MLAN_BSS_ROLE_UAP) && (handle->priv[i]-> media_connected)) { woal_wake_queue(handle-> priv [i]-> netdev); } #endif } } #endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29) */ } } if (skb) dev_kfree_skb_any(skb); } done: LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function write a command/data packet to card. * This function blocks the call until it finishes * * @param pmoal_handle Pointer to the MOAL context * @param pmbuf Pointer to the mlan buffer structure * @param port Port number for sent * @param timeout Timeout value in milliseconds (if 0 the wait is forever) * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_write_data_sync(IN t_void *pmoal_handle, IN pmlan_buffer pmbuf, IN t_u32 port, IN t_u32 timeout) { return woal_write_data_sync((moal_handle *)pmoal_handle, pmbuf, port, timeout); } /** * @brief This function read data packet/event/command from card. * This function blocks the call until it finish * * @param pmoal_handle Pointer to the MOAL context * @param pmbuf Pointer to the mlan buffer structure * @param port Port number for read * @param timeout Timeout value in milliseconds (if 0 the wait is forever) * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_read_data_sync(IN t_void *pmoal_handle, IN OUT pmlan_buffer pmbuf, IN t_u32 port, IN t_u32 timeout) { return woal_read_data_sync((moal_handle *)pmoal_handle, pmbuf, port, timeout); } /** * @brief This function writes data into card register. * * @param pmoal_handle Pointer to the MOAL context * @param reg register offset * @param data value * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_write_reg(IN t_void *pmoal_handle, IN t_u32 reg, IN t_u32 data) { return woal_write_reg((moal_handle *)pmoal_handle, reg, data); } /** * @brief This function reads data from card register. * * @param pmoal_handle Pointer to the MOAL context * @param reg register offset * @param data value * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_read_reg(IN t_void *pmoal_handle, IN t_u32 reg, OUT t_u32 *data) { return woal_read_reg((moal_handle *)pmoal_handle, reg, data); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) /** * @brief This function uploads the packet to the network stack monitor interface * * @param handle Pointer to the MOAL context * @param pmbuf Pointer to mlan_buffer * * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING/MLAN_STATUS_FAILURE */ mlan_status moal_recv_packet_to_mon_if(IN moal_handle *handle, IN pmlan_buffer pmbuf) { mlan_status status = MLAN_STATUS_SUCCESS; struct sk_buff *skb = NULL; struct radiotap_header *rth = NULL; radiotap_info rt_info = { 0 }; t_u8 format = 0; t_u8 bw = 0; t_u8 gi = 0; t_u8 ldpc = 0; t_u8 chan_num; t_u8 band = 0; struct ieee80211_hdr *dot11_hdr = NULL; t_u8 *payload = NULL; t_u32 vht_sig1 = 0; t_u32 vht_sig2 = 0; ENTER(); if (!pmbuf->pdesc) { LEAVE(); return status; } skb = (struct sk_buff *)pmbuf->pdesc; if ((handle->mon_if) && netif_running(handle->mon_if->mon_ndev)) { if (handle->mon_if->radiotap_enabled) { if (skb_headroom(skb) < sizeof(*rth)) { PRINTM(MERROR, "%s No space to add Radio TAP header\n", __func__); status = MLAN_STATUS_FAILURE; handle->mon_if->stats.rx_dropped++; goto done; } dot11_hdr = (struct ieee80211_hdr *)(pmbuf->pbuf + pmbuf->data_offset); memcpy(&rt_info, pmbuf->pbuf + pmbuf->data_offset - sizeof(rt_info), sizeof(rt_info)); ldpc = (rt_info.rate_info.rate_info & 0x20) >> 5; format = (rt_info.rate_info.rate_info & 0x18) >> 3; bw = (rt_info.rate_info.rate_info & 0x06) >> 1; gi = rt_info.rate_info.rate_info & 0x01; skb_push(skb, sizeof(*rth)); rth = (struct radiotap_header *)skb->data; memset(skb->data, 0, sizeof(*rth)); rth->hdr.it_version = PKTHDR_RADIOTAP_VERSION; rth->hdr.it_pad = 0; rth->hdr.it_len = cpu_to_le16(sizeof(*rth)); rth->hdr.it_present = cpu_to_le32((1 << IEEE80211_RADIOTAP_TSFT) | (1 << IEEE80211_RADIOTAP_FLAGS) | (1 << IEEE80211_RADIOTAP_CHANNEL) | (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE) | (1 << IEEE80211_RADIOTAP_ANTENNA)); /** Timstamp */ rth->body.timestamp = cpu_to_le64(jiffies); /** Flags */ rth->body.flags = (rt_info.extra_info.flags & ~(RADIOTAP_FLAGS_USE_SGI_HT | RADIOTAP_FLAGS_WITH_FRAGMENT | RADIOTAP_FLAGS_WEP_ENCRYPTION | RADIOTAP_FLAGS_FAILED_FCS_CHECK)); /** reverse fail fcs, 1 means pass FCS in FW, but means fail FCS in radiotap */ rth->body.flags |= (~rt_info.extra_info. flags) & RADIOTAP_FLAGS_FAILED_FCS_CHECK; if ((format == MLAN_RATE_FORMAT_HT) && (gi == 1)) rth->body.flags |= RADIOTAP_FLAGS_USE_SGI_HT; if (ieee80211_is_mgmt(dot11_hdr->frame_control) || ieee80211_is_data(dot11_hdr->frame_control)) { if ((ieee80211_has_morefrags (dot11_hdr->frame_control)) || (!ieee80211_is_first_frag (dot11_hdr->seq_ctrl))) { rth->body.flags |= RADIOTAP_FLAGS_WITH_FRAGMENT; } } if (ieee80211_is_data(dot11_hdr->frame_control) && ieee80211_has_protected(dot11_hdr->frame_control)) { payload = (t_u8 *)dot11_hdr + ieee80211_hdrlen(dot11_hdr-> frame_control); if (!(*(payload + 3) & 0x20)) /** ExtIV bit shall be 0 for WEP frame */ rth->body.flags |= RADIOTAP_FLAGS_WEP_ENCRYPTION; } /** Rate, t_u8 only apply for LG mode */ if (format == MLAN_RATE_FORMAT_LG) { rth->hdr.it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_RATE); rth->body.rate = rt_info.rate_info.bitrate; } /** Channel */ rth->body.channel.flags = 0; if (rt_info.chan_num) chan_num = rt_info.chan_num; else chan_num = handle->mon_if->band_chan_cfg.channel; band = (chan_num <= 14) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; rth->body.channel.frequency = cpu_to_le16(ieee80211_channel_to_frequency (chan_num, band)); rth->body.channel.flags |= cpu_to_le16((band == IEEE80211_BAND_2GHZ) ? CHANNEL_FLAGS_2GHZ : CHANNEL_FLAGS_5GHZ); if (rth->body.channel. flags & cpu_to_le16(CHANNEL_FLAGS_2GHZ)) rth->body.channel.flags |= cpu_to_le16 (CHANNEL_FLAGS_DYNAMIC_CCK_OFDM); else rth->body.channel.flags |= cpu_to_le16(CHANNEL_FLAGS_OFDM); if (handle->mon_if->chandef.chan && (handle->mon_if->chandef.chan-> flags & (IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_RADAR))) rth->body.channel.flags |= cpu_to_le16 (CHANNEL_FLAGS_ONLY_PASSIVSCAN_ALLOW); /** Antenna */ rth->body.antenna_signal = -(rt_info.nf - rt_info.snr); rth->body.antenna_noise = -rt_info.nf; rth->body.antenna = rt_info.antenna; /** MCS */ if (format == MLAN_RATE_FORMAT_HT) { rth->hdr.it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_MCS); rth->body.u.mcs.known = rt_info.extra_info.mcs_known; rth->body.u.mcs.flags = rt_info.extra_info.mcs_flags; /** MCS mcs */ rth->body.u.mcs.known |= MCS_KNOWN_MCS_INDEX_KNOWN; rth->body.u.mcs.mcs = rt_info.rate_info.mcs_index; /** MCS bw */ rth->body.u.mcs.known |= MCS_KNOWN_BANDWIDTH; rth->body.u.mcs.flags &= ~(0x03); /** Clear, 20MHz as default */ if (bw == 1) rth->body.u.mcs.flags |= RX_BW_40; /** MCS gi */ rth->body.u.mcs.known |= MCS_KNOWN_GUARD_INTERVAL; rth->body.u.mcs.flags &= ~(1 << 2); if (gi) rth->body.u.mcs.flags |= gi << 2; /** MCS FEC */ rth->body.u.mcs.known |= MCS_KNOWN_FEC_TYPE; rth->body.u.mcs.flags &= ~(1 << 4); if (ldpc) rth->body.u.mcs.flags |= ldpc << 4; } /** VHT */ if (format == MLAN_RATE_FORMAT_VHT) { vht_sig1 = rt_info.extra_info.vht_sig1; vht_sig2 = rt_info.extra_info.vht_sig2; /** Present Flag */ rth->hdr.it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_VHT); /** STBC */ rth->body.u.vht.known |= cpu_to_le16(VHT_KNOWN_STBC); if (vht_sig1 & MBIT(3)) rth->body.u.vht.flags |= VHT_FLAG_STBC; /** TXOP_PS_NA */ /** TODO: Not support now */ /** GI */ rth->body.u.vht.known |= cpu_to_le16(VHT_KNOWN_GI); if (vht_sig2 & MBIT(0)) rth->body.u.vht.flags |= VHT_FLAG_SGI; /** SGI NSYM DIS */ rth->body.u.vht.known |= cpu_to_le16(VHT_KNOWN_SGI_NSYM_DIS); if (vht_sig2 & MBIT(1)) rth->body.u.vht.flags |= VHT_FLAG_SGI_NSYM_M10_9; /** LDPC_EXTRA_OFDM_SYM */ /** TODO: Not support now */ /** BEAMFORMED */ rth->body.u.vht.known |= cpu_to_le16(VHT_KNOWN_BEAMFORMED); if (vht_sig2 & MBIT(8)) rth->body.u.vht.flags |= VHT_FLAG_BEAMFORMED; /** BANDWIDTH */ rth->body.u.vht.known |= cpu_to_le16(VHT_KNOWN_BANDWIDTH); if (bw == 1) rth->body.u.vht.bandwidth = RX_BW_40; else if (bw == 2) rth->body.u.vht.bandwidth = RX_BW_80; /** GROUP_ID */ rth->body.u.vht.known |= cpu_to_le16(VHT_KNOWN_GROUP_ID); rth->body.u.vht.group_id = (vht_sig1 & (0x3F0)) >> 4; /** PARTIAL_AID */ /** TODO: Not support now */ /** mcs_nss */ rth->body.u.vht.mcs_nss[0] = (vht_sig2 & (0xF0)) >> 4; rth->body.u.vht.mcs_nss[0] |= (vht_sig1 & (0x1C00)) >> (10 - 4); /** coding */ if (vht_sig2 & MBIT(2)) rth->body.u.vht.coding |= VHT_CODING_LDPC_USER0; } } skb_set_mac_header(skb, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; skb->pkt_type = PACKET_OTHERHOST; skb->protocol = htons(ETH_P_802_2); memset(skb->cb, 0, sizeof(skb->cb)); skb->dev = handle->mon_if->mon_ndev; handle->mon_if->stats.rx_bytes += skb->len; handle->mon_if->stats.rx_packets++; if (in_interrupt()) netif_rx(skb); else netif_rx_ni(skb); status = MLAN_STATUS_PENDING; } done: LEAVE(); return status; } #endif /** * @brief This function uploads the packet to the network stack * * @param pmoal_handle Pointer to the MOAL context * @param pmbuf Pointer to the mlan buffer structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status moal_recv_packet(IN t_void *pmoal_handle, IN pmlan_buffer pmbuf) { mlan_status status = MLAN_STATUS_SUCCESS; moal_private *priv = NULL; struct sk_buff *skb = NULL; moal_handle *handle = (moal_handle *)pmoal_handle; t_u32 max_rx_data_size = MLAN_USB_RX_DATA_BUF_SIZE; dot11_rxcontrol rxcontrol; t_u8 rx_info_flag = MFALSE; int j; ENTER(); if (pmbuf) { priv = woal_bss_index_to_priv(pmoal_handle, pmbuf->bss_index); skb = (struct sk_buff *)pmbuf->pdesc; if (priv) { if (skb) { skb_reserve(skb, pmbuf->data_offset); skb_put(skb, pmbuf->data_len); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) if (pmbuf->flags & MLAN_BUF_FLAG_NET_MONITOR) { status = moal_recv_packet_to_mon_if (pmoal_handle, pmbuf); if (status == MLAN_STATUS_PENDING) atomic_dec(&handle-> mbufalloc_count); goto done; } #endif pmbuf->pdesc = NULL; pmbuf->pbuf = NULL; pmbuf->data_offset = pmbuf->data_len = 0; /* pkt been submit to kernel, no need to free by mlan */ status = MLAN_STATUS_PENDING; atomic_dec(&handle->mbufalloc_count); } else { PRINTM(MERROR, "%s without skb attach!!! pkt_len=%d flags=0x%x\n", __func__, pmbuf->data_len, pmbuf->flags); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) /** drop the packet without skb in monitor mode */ if (pmbuf->flags & MLAN_BUF_FLAG_NET_MONITOR) { PRINTM(MINFO, "%s Drop packet without skb\n", __func__); status = MLAN_STATUS_FAILURE; priv->stats.rx_dropped++; goto done; } #endif skb = dev_alloc_skb(pmbuf->data_len + MLAN_NET_IP_ALIGN); if (!skb) { PRINTM(MERROR, "%s fail to alloc skb\n", __func__); status = MLAN_STATUS_FAILURE; priv->stats.rx_dropped++; goto done; } skb_reserve(skb, MLAN_NET_IP_ALIGN); memcpy(skb->data, (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset), pmbuf->data_len); skb_put(skb, pmbuf->data_len); } skb->dev = priv->netdev; skb->protocol = eth_type_trans(skb, priv->netdev); skb->ip_summed = CHECKSUM_NONE; /* This is only required only in case of 11n and USB as we alloc * a buffer of 4K only if its 11N (to be able to receive 4K AMSDU * packets). In case of SD we allocate buffers based on the size * of packet and hence this is not needed. */ /* Modifying the truesize here as our allocation for each skb is 4K * but we only receive 2K packets and this cause the kernel to start * dropping packets in case where application has allocated buffer * based on 2K size i.e. if there a 64K packet received (in IP * fragments and application allocates 64K to receive this packet * but this packet would almost double up because we allocate each * 1.5K fragment in 4K and pass it up. As soon as the 64K limit * hits kernel will start to drop rest of the fragments. Currently * we fail the Filesndl-ht.scr script for UDP, hence this fix */ if (skb->truesize > max_rx_data_size) skb->truesize += (skb->len - max_rx_data_size); priv->stats.rx_bytes += skb->len; priv->stats.rx_packets++; #ifdef ANDROID_KERNEL if (wakelock_timeout) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) __pm_wakeup_event(&handle->ws, wakelock_timeout); #else wake_lock_timeout(&handle->wake_lock, msecs_to_jiffies (wakelock_timeout)); #endif } #endif if (priv->rx_protocols.protocol_num) { for (j = 0; j < priv->rx_protocols.protocol_num; j++) { if (htons(skb->protocol) == priv->rx_protocols.protocols[j]) rx_info_flag = MTRUE; } } if (rx_info_flag && (skb_tailroom(skb) > sizeof(rxcontrol))) { rxcontrol.datarate = pmbuf->u.rx_info.data_rate; rxcontrol.channel = pmbuf->u.rx_info.channel; rxcontrol.antenna = pmbuf->u.rx_info.antenna; rxcontrol.rssi = pmbuf->u.rx_info.rssi; skb_put(skb, sizeof(dot11_rxcontrol)); memmove(skb->data + sizeof(dot11_rxcontrol), skb->data, skb->len - sizeof(dot11_rxcontrol)); memcpy(skb->data, &rxcontrol, sizeof(dot11_rxcontrol)); } if (in_interrupt()) netif_rx(skb); else { if (atomic_read(&handle->rx_pending) > MAX_RX_PENDING_THRHLD) netif_rx(skb); else netif_rx_ni(skb); } } } done: LEAVE(); return status; } /** * @brief This function handles event receive * * @param pmoal_handle Pointer to the MOAL context * @param pmevent Pointer to the mlan event structure * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_recv_event(IN t_void *pmoal_handle, IN pmlan_event pmevent) { #ifdef STA_SUPPORT int custom_len = 0; #ifdef STA_CFG80211 unsigned long flags; #endif #endif moal_private *priv = NULL; #if defined(STA_SUPPORT) || defined(UAP_SUPPORT) moal_private *pmpriv = NULL; #endif char *pevent = NULL; #if defined(STA_WEXT) || defined(UAP_WEXT) #if defined(STA_SUPPORT) || defined(UAP_WEXT) #if defined(UAP_SUPPORT) || defined(STA_WEXT) union iwreq_data wrqu; #endif #endif #endif mlan_ds_ps_info pm_info; t_u8 category = 0; t_u8 action_code = 0; char *buf; t_u8 peer_addr[ETH_ALEN] = { 0 }; moal_wnm_tm_msmt *tm_ind; moal_wlan_802_11_header *header; moal_timestamps *tsstamp; t_u8 payload_len, i; moal_ptp_context *ptp_context; #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) t_u8 enable = 1; #endif #endif t_u8 *req_ie = NULL; t_u16 ie_len = 0; apinfo *pinfo = NULL, *req_tlv = NULL; MrvlIEtypesHeader_t *tlv = NULL; t_u16 tlv_type = 0, tlv_len = 0, tlv_buf_left = 0; #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) struct cfg80211_roam_info roam_info; #endif #if defined(UAP_CFG80211) || defined(STA_CFG80211) chan_band_info *pchan_info = NULL; #endif t_u8 channel_status; moal_private *remain_priv = NULL; ENTER(); if ((pmevent->event_id != MLAN_EVENT_ID_DRV_DEFER_RX_WORK) && (pmevent->event_id != MLAN_EVENT_ID_DRV_DEFER_HANDLING) && (pmevent->event_id != MLAN_EVENT_ID_DRV_MGMT_FRAME)) PRINTM(MEVENT, "event id:0x%x\n", pmevent->event_id); if (pmevent->event_id == MLAN_EVENT_ID_FW_DUMP_INFO) { woal_store_firmware_dump(pmoal_handle, pmevent); goto done; } if (pmevent->event_id == MLAN_EVENT_ID_STORE_HOST_CMD_RESP) { woal_save_host_cmdresp((moal_handle *)pmoal_handle, (mlan_cmdresp_event *) pmevent); goto done; } priv = woal_bss_index_to_priv(pmoal_handle, pmevent->bss_index); if (priv == NULL) { PRINTM(MERROR, "%s: priv is null\n", __func__); goto done; } if (priv->netdev == NULL) { PRINTM(MERROR, "%s: netdev is null\n", __func__); goto done; } pevent = kzalloc(512, GFP_ATOMIC); if (!pevent) { PRINTM(MERROR, "Failed to alloc memory for pevent\n"); goto done; } switch (pmevent->event_id) { #ifdef STA_SUPPORT case MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED: priv->is_adhoc_link_sensed = MTRUE; if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); woal_wake_queue(priv->netdev); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_ADHOC_LINK_SENSED); #endif woal_broadcast_event(priv, CUS_EVT_ADHOC_LINK_SENSED, strlen(CUS_EVT_ADHOC_LINK_SENSED)); break; case MLAN_EVENT_ID_FW_ADHOC_LINK_LOST: woal_stop_queue(priv->netdev); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); priv->is_adhoc_link_sensed = MFALSE; #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_ADHOC_LINK_LOST); #endif woal_broadcast_event(priv, CUS_EVT_ADHOC_LINK_LOST, strlen(CUS_EVT_ADHOC_LINK_LOST)); break; case MLAN_EVENT_ID_DRV_CONNECTED: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext) && pmevent->event_len == ETH_ALEN) { memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); memcpy(wrqu.ap_addr.sa_data, pmevent->event_buf, ETH_ALEN); wrqu.ap_addr.sa_family = ARPHRD_ETHER; wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL); } #endif #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { memcpy(priv->cfg_bssid, pmevent->event_buf, ETH_ALEN); woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, PASSIVE_SCAN_CHAN_TIME, MIN_SPECIFIC_SCAN_CHAN_TIME); } #endif custom_len = strlen(CUS_EVT_AP_CONNECTED); memmove(pmevent->event_buf + custom_len, pmevent->event_buf, pmevent->event_len); memcpy(pmevent->event_buf, CUS_EVT_AP_CONNECTED, custom_len); pmevent->event_len += custom_len; woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); woal_update_dscp_mapping(priv); priv->media_connected = MTRUE; if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); woal_wake_queue(priv->netdev); break; case MLAN_EVENT_ID_DRV_SCAN_REPORT: PRINTM(MINFO, "Scan report\n"); if (priv->report_scan_result) { priv->report_scan_result = MFALSE; #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { if (priv->phandle->scan_request) { PRINTM(MINFO, "Reporting scan results\n"); woal_inform_bss_from_scan_result(priv, NULL, MOAL_NO_WAIT); if (!priv->phandle->first_scan_done) { priv->phandle->first_scan_done = MTRUE; woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, PASSIVE_SCAN_CHAN_TIME, SPECIFIC_SCAN_CHAN_TIME); } spin_lock_irqsave(&priv->phandle-> scan_req_lock, flags); if (priv->phandle->scan_request) { woal_cfg80211_scan_done(priv-> phandle-> scan_request, MFALSE); priv->phandle->scan_request = NULL; } spin_unlock_irqrestore(&priv->phandle-> scan_req_lock, flags); } } #endif /* STA_CFG80211 */ #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) { memset(&wrqu, 0, sizeof(union iwreq_data)); wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL); } #endif woal_broadcast_event(priv, (t_u8 *)&pmevent->event_id, sizeof(mlan_event_id)); } if (priv->phandle->scan_pending_on_block == MTRUE) { priv->phandle->scan_pending_on_block = MFALSE; priv->phandle->scan_priv = NULL; MOAL_REL_SEMAPHORE(&priv->phandle->async_sem); } break; case MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM: memmove((pmevent->event_buf + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1), pmevent->event_buf, pmevent->event_len); memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_OBSS_SCAN_PARAM, strlen(CUS_EVT_OBSS_SCAN_PARAM)); pmevent->event_buf[strlen(CUS_EVT_OBSS_SCAN_PARAM)] = 0; woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len + strlen(CUS_EVT_OBSS_SCAN_PARAM)); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) { memset(&wrqu, 0, sizeof(union iwreq_data)); wrqu.data.pointer = pmevent->event_buf; wrqu.data.length = pmevent->event_len + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1; wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, pmevent->event_buf); } #endif break; case MLAN_EVENT_ID_FW_BW_CHANGED: memmove((pmevent->event_buf + strlen(CUS_EVT_BW_CHANGED) + 1), pmevent->event_buf, pmevent->event_len); memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_BW_CHANGED, strlen(CUS_EVT_BW_CHANGED)); pmevent->event_buf[strlen(CUS_EVT_BW_CHANGED)] = 0; woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len + strlen(CUS_EVT_BW_CHANGED)); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) { memset(&wrqu, 0, sizeof(union iwreq_data)); wrqu.data.pointer = pmevent->event_buf; wrqu.data.length = pmevent->event_len + strlen(CUS_EVT_BW_CHANGED) + 1; wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, pmevent->event_buf); } #endif break; case MLAN_EVENT_ID_FW_DISCONNECTED: woal_send_disconnect_to_system(priv, (t_u16)*pmevent->event_buf); #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) priv->auth_flag = 0; priv->host_mlme = MFALSE; #endif #endif #ifdef STA_WEXT /* Reset wireless stats signal info */ if (IS_STA_WEXT(cfg80211_wext)) { priv->w_stats.qual.level = 0; priv->w_stats.qual.noise = 0; } #endif #ifdef REASSOCIATION if (priv->reassoc_on == MTRUE) { PRINTM(MINFO, "Reassoc: trigger the timer\n"); priv->reassoc_required = MTRUE; priv->phandle->is_reassoc_timer_set = MTRUE; woal_mod_timer(&priv->phandle->reassoc_timer, REASSOC_TIMER_DEFAULT); } else { priv->rate_index = AUTO_RATE; } #endif /* REASSOCIATION */ break; case MLAN_EVENT_ID_FW_MIC_ERR_UNI: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) { #if WIRELESS_EXT >= 18 woal_send_mic_error_event(priv, MLAN_EVENT_ID_FW_MIC_ERR_UNI); #else woal_send_iwevcustom_event(priv, CUS_EVT_MLME_MIC_ERR_UNI); #endif } #endif /* STA_WEXT */ #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, NL80211_KEYTYPE_PAIRWISE, -1, NULL, GFP_KERNEL); } #endif woal_broadcast_event(priv, CUS_EVT_MLME_MIC_ERR_UNI, strlen(CUS_EVT_MLME_MIC_ERR_UNI)); break; case MLAN_EVENT_ID_FW_MIC_ERR_MUL: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) { #if WIRELESS_EXT >= 18 woal_send_mic_error_event(priv, MLAN_EVENT_ID_FW_MIC_ERR_MUL); #else woal_send_iwevcustom_event(priv, CUS_EVT_MLME_MIC_ERR_MUL); #endif } #endif /* STA_WEXT */ #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, NL80211_KEYTYPE_GROUP, -1, NULL, GFP_KERNEL); } #endif woal_broadcast_event(priv, CUS_EVT_MLME_MIC_ERR_MUL, strlen(CUS_EVT_MLME_MIC_ERR_MUL)); break; case MLAN_EVENT_ID_FW_BCN_RSSI_LOW: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_BEACON_RSSI_LOW); #endif #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { #if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) cfg80211_cqm_rssi_notify(priv->netdev, NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) *(t_s16 *)pmevent->event_buf, #endif GFP_KERNEL); priv->last_event |= EVENT_BCN_RSSI_LOW; #endif if (!hw_test && priv->roaming_enabled) woal_config_bgscan_and_rssi(priv, MTRUE); #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) woal_cfg80211_rssi_monitor_event(priv, *(t_s16 *)pmevent-> event_buf); #endif } #endif woal_broadcast_event(priv, CUS_EVT_BEACON_RSSI_LOW, strlen(CUS_EVT_BEACON_RSSI_LOW)); break; case MLAN_EVENT_ID_FW_BCN_RSSI_HIGH: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_BEACON_RSSI_HIGH); #endif #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { if (!priv->mrvl_rssi_low) { #if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) cfg80211_cqm_rssi_notify(priv->netdev, NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) *(t_s16 *)pmevent-> event_buf, #endif GFP_KERNEL); #endif woal_set_rssi_threshold(priv, MLAN_EVENT_ID_FW_BCN_RSSI_HIGH, MOAL_NO_WAIT); } #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) woal_cfg80211_rssi_monitor_event(priv, *(t_s16 *)pmevent-> event_buf); #endif } #endif woal_broadcast_event(priv, CUS_EVT_BEACON_RSSI_HIGH, strlen(CUS_EVT_BEACON_RSSI_HIGH)); break; case MLAN_EVENT_ID_FW_BCN_SNR_LOW: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_BEACON_SNR_LOW); #endif woal_broadcast_event(priv, CUS_EVT_BEACON_SNR_LOW, strlen(CUS_EVT_BEACON_SNR_LOW)); break; case MLAN_EVENT_ID_FW_BCN_SNR_HIGH: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_BEACON_SNR_HIGH); #endif woal_broadcast_event(priv, CUS_EVT_BEACON_SNR_HIGH, strlen(CUS_EVT_BEACON_SNR_HIGH)); break; case MLAN_EVENT_ID_FW_MAX_FAIL: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_MAX_FAIL); #endif woal_broadcast_event(priv, CUS_EVT_MAX_FAIL, strlen(CUS_EVT_MAX_FAIL)); break; case MLAN_EVENT_ID_FW_DATA_RSSI_LOW: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_DATA_RSSI_LOW); #endif woal_broadcast_event(priv, CUS_EVT_DATA_RSSI_LOW, strlen(CUS_EVT_DATA_RSSI_LOW)); break; case MLAN_EVENT_ID_FW_DATA_SNR_LOW: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_DATA_SNR_LOW); #endif woal_broadcast_event(priv, CUS_EVT_DATA_SNR_LOW, strlen(CUS_EVT_DATA_SNR_LOW)); break; case MLAN_EVENT_ID_FW_DATA_RSSI_HIGH: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_DATA_RSSI_HIGH); #endif woal_broadcast_event(priv, CUS_EVT_DATA_RSSI_HIGH, strlen(CUS_EVT_DATA_RSSI_HIGH)); break; case MLAN_EVENT_ID_FW_DATA_SNR_HIGH: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_DATA_SNR_HIGH); #endif woal_broadcast_event(priv, CUS_EVT_DATA_SNR_HIGH, strlen(CUS_EVT_DATA_SNR_HIGH)); break; case MLAN_EVENT_ID_FW_LINK_QUALITY: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_LINK_QUALITY); #endif woal_broadcast_event(priv, CUS_EVT_LINK_QUALITY, strlen(CUS_EVT_LINK_QUALITY)); break; case MLAN_EVENT_ID_FW_PORT_RELEASE: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_PORT_RELEASE); #endif woal_broadcast_event(priv, CUS_EVT_PORT_RELEASE, strlen(CUS_EVT_PORT_RELEASE)); break; case MLAN_EVENT_ID_FW_PRE_BCN_LOST: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_PRE_BEACON_LOST); #endif #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) if (IS_STA_CFG80211(cfg80211_wext)) { struct cfg80211_bss *bss = NULL; bss = cfg80211_get_bss(priv->wdev->wiphy, NULL, priv->cfg_bssid, NULL, 0, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); if (bss) cfg80211_unlink_bss(priv->wdev->wiphy, bss); if (!hw_test && priv->roaming_enabled) woal_config_bgscan_and_rssi(priv, MFALSE); priv->last_event |= EVENT_PRE_BCN_LOST; } #endif #endif woal_broadcast_event(priv, CUS_EVT_PRE_BEACON_LOST, strlen(CUS_EVT_PRE_BEACON_LOST)); break; case MLAN_EVENT_ID_FW_DEBUG_INFO: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, pmevent->event_buf); #endif memmove((pmevent->event_buf + strlen(FW_DEBUG_INFO) + 1), pmevent->event_buf, pmevent->event_len); memcpy(pmevent->event_buf, (t_u8 *)FW_DEBUG_INFO, strlen(FW_DEBUG_INFO)); pmevent->event_buf[strlen(FW_DEBUG_INFO)] = 0; woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len + strlen(FW_DEBUG_INFO) + 1); break; case MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, WMM_CONFIG_CHANGE_INDICATION); #endif woal_broadcast_event(priv, WMM_CONFIG_CHANGE_INDICATION, strlen(WMM_CONFIG_CHANGE_INDICATION)); break; case MLAN_EVENT_ID_DRV_REPORT_STRING: PRINTM(MINFO, "Report string %s\n", pmevent->event_buf); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, pmevent->event_buf); #endif woal_broadcast_event(priv, pmevent->event_buf, strlen(pmevent->event_buf)); break; case MLAN_EVENT_ID_FW_WEP_ICV_ERR: DBG_HEXDUMP(MCMD_D, "WEP ICV error", pmevent->event_buf, pmevent->event_len); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_WEP_ICV_ERR); #endif woal_broadcast_event(priv, CUS_EVT_WEP_ICV_ERR, strlen(CUS_EVT_WEP_ICV_ERR)); break; case MLAN_EVENT_ID_DRV_DEFER_HANDLING: queue_work(priv->phandle->workqueue, &priv->phandle->main_work); break; case MLAN_EVENT_ID_DRV_FLUSH_RX_WORK: if (napi) { napi_synchronize(&priv->phandle->napi_rx); break; } flush_workqueue(priv->phandle->rx_workqueue); break; case MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK: flush_workqueue(priv->phandle->workqueue); break; case MLAN_EVENT_ID_DRV_DEFER_RX_WORK: if (napi) { napi_schedule(&priv->phandle->napi_rx); break; } queue_work(priv->phandle->rx_workqueue, &priv->phandle->rx_work); break; case MLAN_EVENT_ID_DRV_DBG_DUMP: priv->phandle->driver_state = MTRUE; woal_moal_debug_info(priv, NULL, MFALSE); woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG, strlen(CUS_EVT_DRIVER_HANG)); #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) woal_cfg80211_vendor_event(priv, event_hang, CUS_EVT_DRIVER_HANG, strlen(CUS_EVT_DRIVER_HANG)); #endif #endif woal_process_hang(priv->phandle); break; case MLAN_EVENT_ID_FW_BG_SCAN: if (priv->media_connected == MTRUE) priv->bg_scan_start = MFALSE; priv->bg_scan_reported = MTRUE; #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) { memset(&wrqu, 0, sizeof(union iwreq_data)); wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, NULL); } #endif #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { priv->last_event |= EVENT_BG_SCAN_REPORT; #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) if (priv->sched_scanning && !priv->phandle->cfg80211_suspend) { mlan_scan_resp scan_resp; woal_get_scan_table(priv, MOAL_NO_WAIT, &scan_resp); PRINTM(MIOCTL, "Trigger mlan get bgscan result\n"); } #endif if (!hw_test && priv->roaming_enabled #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) && !priv->phandle->cfg80211_suspend #endif ) { priv->roaming_required = MTRUE; #ifdef ANDROID_KERNEL #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) __pm_wakeup_event(&priv->phandle->ws, ROAMING_WAKE_LOCK_TIMEOUT); #else wake_lock_timeout(&priv->phandle->wake_lock, msecs_to_jiffies (ROAMING_WAKE_LOCK_TIMEOUT)); #endif #endif wake_up_interruptible(&priv->phandle-> reassoc_thread.wait_q); } } #endif break; case MLAN_EVENT_ID_FW_BG_SCAN_STOPPED: #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) if (IS_STA_CFG80211(cfg80211_wext)) { if (priv->sched_scanning) { #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 14, 6) priv->phandle->rx_bgscan_stop = MTRUE; priv->phandle->bg_scan_priv = priv; queue_work(priv->phandle->rx_workqueue, &priv->phandle->rx_work); #else if (rtnl_is_locked()) cfg80211_sched_scan_stopped_rtnl(priv-> wdev-> wiphy #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) , 0 #endif ); else cfg80211_sched_scan_stopped(priv->wdev-> wiphy #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) , 0 #endif ); priv->sched_scanning = MFALSE; #endif PRINTM(MEVENT, "Sched_Scan stopped\n"); } } #endif #endif break; case MLAN_EVENT_ID_DRV_BGSCAN_RESULT: #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) if (IS_STA_CFG80211(cfg80211_wext)) { if (priv->sched_scanning && !priv->phandle->cfg80211_suspend) { woal_inform_bss_from_scan_result(priv, NULL, MOAL_NO_WAIT); cfg80211_sched_scan_results(priv->wdev->wiphy #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) , priv->bg_scan_reqid #endif ); priv->last_event = 0; PRINTM(MEVENT, "Reporting Sched_Scan results\n"); #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 14, 6) priv->phandle->rx_bgscan_stop = MTRUE; priv->phandle->bg_scan_priv = priv; queue_work(priv->phandle->rx_workqueue, &priv->phandle->rx_work); #else if (rtnl_is_locked()) cfg80211_sched_scan_stopped_rtnl(priv-> wdev-> wiphy #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) , 0 #endif ); else cfg80211_sched_scan_stopped(priv->wdev-> wiphy #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) , 0 #endif ); priv->sched_scanning = MFALSE; #endif PRINTM(MEVENT, "Sched_Scan stopped\n"); } } #endif #endif break; #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) case MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY: if (priv->phandle->is_cac_timer_set) { t_u8 radar_detected = pmevent->event_buf[0]; PRINTM(MEVENT, "%s radar found when CAC \n", radar_detected ? "" : "No"); moal_stop_timer(priv->phandle, &priv->phandle->cac_timer); priv->phandle->is_cac_timer_set = MFALSE; if (radar_detected) { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); #else cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); #endif cfg80211_radar_event(priv->wdev->wiphy, &priv->phandle-> dfs_channel, GFP_KERNEL); } else { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); #else cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); #endif } memset(&priv->phandle->dfs_channel, 0, sizeof(struct cfg80211_chan_def)); priv->phandle->cac_bss_index = 0xff; } break; case MLAN_EVENT_ID_FW_RADAR_DETECTED: if (priv->phandle->is_cac_timer_set) { if (priv->bss_index == priv->phandle->cac_bss_index) { PRINTM(MEVENT, "radar detected during CAC \n"); woal_cancel_timer(&priv->phandle->cac_timer); priv->phandle->is_cac_timer_set = MFALSE; /* downstream: cancel the unfinished CAC in Firmware */ woal_11h_cancel_chan_report_ioctl(priv, MOAL_NO_WAIT); /* upstream: inform cfg80211 */ #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); #else cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); #endif cfg80211_radar_event(priv->wdev->wiphy, &priv->phandle-> dfs_channel, GFP_KERNEL); memset(&priv->phandle->dfs_channel, 0, sizeof(priv->phandle->dfs_channel)); priv->phandle->cac_bss_index = 0xff; } else { PRINTM(MERROR, " Radar event for incorrect inferface \n"); } } else { PRINTM(MEVENT, "radar detected during BSS active \n"); #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) if (dfs_offload) woal_cfg80211_dfs_vendor_event(priv, event_dfs_radar_detected, &priv->chan); else #endif cfg80211_radar_event(priv->wdev->wiphy, &priv->chan, GFP_KERNEL); } break; #endif #endif case MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN: #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext)) woal_send_iwevcustom_event(priv, CUS_EVT_CHANNEL_SWITCH_ANN); #endif woal_broadcast_event(priv, CUS_EVT_CHANNEL_SWITCH_ANN, strlen(CUS_EVT_CHANNEL_SWITCH_ANN)); break; #endif /* STA_SUPPORT */ case MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE: #if defined(UAP_CFG80211) || defined(STA_CFG80211) pchan_info = (chan_band_info *) pmevent->event_buf; if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { PRINTM(MMSG, "CSA/ECSA: Switch to new channel %d complete!\n", pchan_info->channel); priv->channel = pchan_info->channel; #ifdef UAP_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,12,0) if (priv->csa_chan.chan && (pchan_info->channel == priv->csa_chan.chan->hw_value)) { memcpy(&priv->chan, &priv->csa_chan, sizeof(struct cfg80211_chan_def)); } #endif #endif #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,8,0) if (MFALSE #ifdef UAP_CFG80211 || priv->uap_host_based #endif #ifdef STA_CFG80211 || priv->sme_current.ssid_len #endif ) { PRINTM(MEVENT, "CHAN_SWITCH: 11n=%d, chan=%d, center_chan=%d, band=%d, width=%d, 2Offset=%d\n", pchan_info->is_11n_enabled, pchan_info->channel, pchan_info->center_chan, pchan_info->bandcfg.chanBand, pchan_info->bandcfg.chanWidth, pchan_info->bandcfg.chan2Offset); woal_cfg80211_notify_channel(priv, pchan_info); } #endif } #endif #ifdef UAP_SUPPORT if (priv->bss_role == MLAN_BSS_ROLE_UAP) { if (priv->uap_tx_blocked) { if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); woal_start_queue(priv->netdev); priv->uap_tx_blocked = MFALSE; } priv->phandle->chsw_wait_q_woken = MTRUE; wake_up_interruptible(&priv->phandle->chsw_wait_q); } #endif break; case MLAN_EVENT_ID_FW_STOP_TX: woal_stop_queue(priv->netdev); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); break; case MLAN_EVENT_ID_FW_START_TX: if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); woal_wake_queue(priv->netdev); break; case MLAN_EVENT_ID_FW_HS_WAKEUP: /* simulate HSCFG_CANCEL command */ woal_cancel_hs(priv, MOAL_NO_WAIT); #ifdef STA_SUPPORT pmpriv = woal_get_priv((moal_handle *)pmoal_handle, MLAN_BSS_ROLE_STA); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext) && pmpriv) woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_WAKEUP); #endif /* STA_WEXT */ if (pmpriv) woal_broadcast_event(pmpriv, CUS_EVT_HS_WAKEUP, strlen(CUS_EVT_HS_WAKEUP)); #endif /*STA_SUPPORT */ #ifdef UAP_SUPPORT pmpriv = woal_get_priv((moal_handle *)pmoal_handle, MLAN_BSS_ROLE_UAP); if (pmpriv) { pmevent->event_id = UAP_EVENT_ID_HS_WAKEUP; woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, sizeof(t_u32)); } #endif /* UAP_SUPPORT */ break; case MLAN_EVENT_ID_DRV_HS_ACTIVATED: #ifdef STA_SUPPORT pmpriv = woal_get_priv((moal_handle *)pmoal_handle, MLAN_BSS_ROLE_STA); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext) && pmpriv) woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_ACTIVATED); #endif /* STA_WEXT */ if (pmpriv) woal_broadcast_event(pmpriv, CUS_EVT_HS_ACTIVATED, strlen(CUS_EVT_HS_ACTIVATED)); #endif /* STA_SUPPORT */ #if defined(UAP_SUPPORT) pmpriv = woal_get_priv((moal_handle *)pmoal_handle, MLAN_BSS_ROLE_UAP); if (pmpriv) { pmevent->event_id = UAP_EVENT_ID_DRV_HS_ACTIVATED; woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, sizeof(t_u32)); } #endif if (priv->phandle->suspend_fail == MFALSE) { woal_get_pm_info(priv, &pm_info); if (pm_info.is_suspend_allowed == MTRUE) { priv->phandle->hs_activated = MTRUE; #ifdef MMC_PM_FUNC_SUSPENDED woal_wlan_is_suspended(priv->phandle); #endif } priv->phandle->hs_activate_wait_q_woken = MTRUE; wake_up(&priv->phandle->hs_activate_wait_q); } break; case MLAN_EVENT_ID_DRV_HS_DEACTIVATED: #ifdef STA_SUPPORT pmpriv = woal_get_priv((moal_handle *)pmoal_handle, MLAN_BSS_ROLE_STA); #ifdef STA_WEXT if (IS_STA_WEXT(cfg80211_wext) && pmpriv) woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_DEACTIVATED); #endif /* STA_WEXT */ if (pmpriv) woal_broadcast_event(pmpriv, CUS_EVT_HS_DEACTIVATED, strlen(CUS_EVT_HS_DEACTIVATED)); #endif /* STA_SUPPORT */ #if defined(UAP_SUPPORT) pmpriv = woal_get_priv((moal_handle *)pmoal_handle, MLAN_BSS_ROLE_UAP); if (pmpriv) { pmevent->event_id = UAP_EVENT_ID_DRV_HS_DEACTIVATED; woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, sizeof(t_u32)); } #endif priv->phandle->hs_activated = MFALSE; break; #ifdef UAP_SUPPORT case MLAN_EVENT_ID_UAP_FW_BSS_START: if (priv->hist_data) woal_hist_data_reset(priv); priv->bss_started = MTRUE; priv->skip_cac = MFALSE; if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); woal_start_queue(priv->netdev); memcpy(priv->current_addr, pmevent->event_buf + 6, ETH_ALEN); memcpy(priv->netdev->dev_addr, priv->current_addr, ETH_ALEN); woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); #ifdef STA_SUPPORT #ifdef STA_CFG80211 pmpriv = woal_get_priv((moal_handle *)pmoal_handle, MLAN_BSS_ROLE_STA); if (IS_STA_CFG80211(cfg80211_wext) && pmpriv) woal_set_scan_time(pmpriv, ACTIVE_SCAN_CHAN_TIME, PASSIVE_SCAN_CHAN_TIME, MIN_SPECIFIC_SCAN_CHAN_TIME); #endif #endif #ifdef UAP_CFG80211 #if defined(DFS_TESTING_SUPPORT) #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) if (priv->chan_under_nop) { PRINTM(MMSG, "Channel Under Nop: notify cfg80211 new channel=%d\n", priv->channel); cfg80211_ch_switch_notify(priv->netdev, &priv->chan); priv->chan_under_nop = MFALSE; } #endif #endif #endif break; #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) case MLAN_EVENT_ID_DRV_UAP_CHAN_INFO: #ifdef UAP_CFG80211 if (IS_UAP_CFG80211(cfg80211_wext)) { chan_band_info *pchan_info = (chan_band_info *) pmevent->event_buf; PRINTM(MEVENT, "UAP: 11n=%d, chan=%d, center_chan=%d, band=%d, width=%d, 2Offset=%d\n", pchan_info->is_11n_enabled, pchan_info->channel, pchan_info->center_chan, pchan_info->bandcfg.chanBand, pchan_info->bandcfg.chanWidth, pchan_info->bandcfg.chan2Offset); if (priv->uap_host_based && (priv->channel != pchan_info->channel)) woal_cfg80211_notify_channel(priv, pchan_info); } #endif break; #endif case MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE: priv->media_connected = MTRUE; if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); woal_wake_queue(priv->netdev); woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); break; case MLAN_EVENT_ID_UAP_FW_BSS_IDLE: priv->media_connected = MFALSE; woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); break; #if defined(STA_CFG80211) || defined(UAP_CFG80211) case MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED: if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { PRINTM(MEVENT, "FW_REMAIN_ON_CHANNEL_EXPIRED cookie = %#llx\n", priv->phandle->cookie); priv->phandle->remain_on_channel = MFALSE; if (priv->phandle->cookie && !priv->phandle->is_remain_timer_set) { cfg80211_remain_on_channel_expired( #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) priv-> netdev, #else priv-> wdev, #endif priv-> phandle-> cookie, &priv-> phandle-> chan, #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) priv-> phandle-> channel_type, #endif GFP_ATOMIC); priv->phandle->cookie = 0; } } break; #endif case MLAN_EVENT_ID_UAP_FW_STA_CONNECT: #if defined(STA_CFG80211) || defined(UAP_CFG80211) if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { struct station_info sinfo = { 0 }; t_u8 addr[ETH_ALEN]; sinfo.filled = 0; sinfo.generation = 0; /* copy the station mac address */ memset(addr, 0xFF, ETH_ALEN); memcpy(addr, pmevent->event_buf, ETH_ALEN); /** these field add in kernel 3.2, but some * kernel do have the pacth to support it, * like T3T and pxa978T 3.0.31 JB, these * patch are needed to support * wpa_supplicant 2.x */ #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 31) if (pmevent->event_len > ETH_ALEN) { #if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 0, 0) /* set station info filled flag */ sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; #endif #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) sinfo.pertid = NULL; #endif /* get the assoc request ies and length */ sinfo.assoc_req_ies = (const t_u8 *)(pmevent->event_buf + ETH_ALEN); sinfo.assoc_req_ies_len = pmevent->event_len - ETH_ALEN; } #endif /* KERNEL_VERSION */ if (priv->netdev && priv->wdev) cfg80211_new_sta(priv->netdev, (t_u8 *)addr, &sinfo, GFP_KERNEL); } #endif /* UAP_CFG80211 */ memmove((pmevent->event_buf + strlen(CUS_EVT_STA_CONNECTED) + 1), pmevent->event_buf, pmevent->event_len); memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_STA_CONNECTED, strlen(CUS_EVT_STA_CONNECTED)); pmevent->event_buf[strlen(CUS_EVT_STA_CONNECTED)] = 0; woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len + strlen(CUS_EVT_STA_CONNECTED)); #ifdef UAP_WEXT if (IS_UAP_WEXT(cfg80211_wext)) { memset(&wrqu, 0, sizeof(union iwreq_data)); wrqu.data.pointer = pmevent->event_buf; if ((pmevent->event_len + strlen(CUS_EVT_STA_CONNECTED) + 1) > IW_CUSTOM_MAX) wrqu.data.length = ETH_ALEN + strlen(CUS_EVT_STA_CONNECTED) + 1; else wrqu.data.length = pmevent->event_len + strlen(CUS_EVT_STA_CONNECTED) + 1; wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, pmevent->event_buf); } #endif /* UAP_WEXT */ break; case MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT: #ifdef UAP_CFG80211 if (IS_UAP_CFG80211(cfg80211_wext)) { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) /* skip 2 bytes extra header will get the mac address */ if (priv->netdev && priv->wdev) cfg80211_del_sta(priv->netdev, pmevent->event_buf + 2, GFP_KERNEL); #endif /* KERNEL_VERSION */ } #endif /* UAP_CFG80211 */ memmove((pmevent->event_buf + strlen(CUS_EVT_STA_DISCONNECTED) + 1), pmevent->event_buf, pmevent->event_len); memcpy(pmevent->event_buf, (t_u8 *)CUS_EVT_STA_DISCONNECTED, strlen(CUS_EVT_STA_DISCONNECTED)); pmevent->event_buf[strlen(CUS_EVT_STA_DISCONNECTED)] = 0; woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len + strlen(CUS_EVT_STA_DISCONNECTED)); #ifdef UAP_WEXT if (IS_UAP_WEXT(cfg80211_wext)) { memset(&wrqu, 0, sizeof(union iwreq_data)); wrqu.data.pointer = pmevent->event_buf; wrqu.data.length = pmevent->event_len + strlen(CUS_EVT_STA_DISCONNECTED) + 1; wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, pmevent->event_buf); } #endif /* UAP_WEXT */ break; case MLAN_EVENT_ID_DRV_MGMT_FRAME: buf = (t_u8 *)(pmevent->event_buf + sizeof(pmevent->event_id)); payload_len = pmevent->event_len - sizeof(pmevent->event_id) - sizeof(moal_timestamps); category = *(buf + sizeof(moal_wlan_802_11_header)); action_code = *(buf + sizeof(moal_wlan_802_11_header) + 1); header = (moal_wlan_802_11_header *) (buf); tm_ind = (moal_wnm_tm_msmt *) (buf + sizeof(moal_wlan_802_11_header) + 2); tsstamp = (moal_timestamps *) ((t_u8 *)buf + payload_len); memcpy(peer_addr, (t_u8 *)&header->addr2, ETH_ALEN); if ((category == IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM) && (action_code == 0x1)) { sprintf(pevent, "%s " FULL_MACSTR " %u %u %u %u %u %u %u %u %u %u %lu %llu", CUS_EVT_TM_FRAME_INDICATION, FULL_MAC2STR(peer_addr), tm_ind->dialog_token, tsstamp->t2, tsstamp->t2_err, tsstamp->t3, tsstamp->t3_err, tm_ind->follow_up_dialog_token, tm_ind->t1, tm_ind->t1_err, tm_ind->t4, tm_ind->t4_err, (unsigned long)tsstamp->t2 * 10L, tsstamp->ingress_time); if (payload_len > (sizeof(moal_wnm_tm_msmt) + sizeof(moal_wlan_802_11_header) + 2)) { ptp_context = (moal_ptp_context *) (buf + sizeof (moal_wlan_802_11_header) + 2 + sizeof (moal_wnm_tm_msmt)); sprintf(pevent + strlen(pevent), " %02x %02x", ptp_context->vendor_specific, ptp_context->length); for (i = 0; i < ptp_context->length; i++) sprintf(pevent + strlen(pevent), " %02x", ptp_context->data[i]); } woal_broadcast_event(priv, pevent, strlen(pevent)); } #ifdef UAP_WEXT if (IS_UAP_WEXT(cfg80211_wext)) { woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); } #endif /* UAP_WEXT */ #if defined(STA_CFG80211) || defined(UAP_CFG80211) if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) if (priv->netdev && priv->netdev->ieee80211_ptr->wiphy->mgmt_stypes && priv->mgmt_subtype_mask) { /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl */ #define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) t_u8 *pkt; int freq = priv->phandle-> remain_on_channel ? priv->phandle->chan. center_freq : woal_get_active_intf_freq(priv); if (!freq) { if (!priv->phandle->chan.center_freq) { PRINTM(MINFO, "Skip to report mgmt packet to cfg80211\n"); break; } freq = priv->phandle->chan.center_freq; } pkt = ((t_u8 *)pmevent->event_buf + sizeof(pmevent->event_id)); /* move addr4 */ memmove(pkt + PACKET_ADDR4_POS, pkt + PACKET_ADDR4_POS + ETH_ALEN, pmevent->event_len - sizeof(pmevent->event_id) - PACKET_ADDR4_POS - ETH_ALEN); #ifdef WIFI_DIRECT_SUPPORT if (ieee80211_is_action (((struct ieee80211_mgmt *)pkt)-> frame_control)) woal_cfg80211_display_p2p_actframe(pkt, pmevent-> event_len - sizeof (pmevent-> event_id) - MLAN_MAC_ADDR_LENGTH, ieee80211_get_channel (priv-> wdev-> wiphy, freq), MFALSE); #endif #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) /**Forward Deauth, Auth and disassoc frame to Host*/ if (priv->host_mlme && (ieee80211_is_deauth (((struct ieee80211_mgmt *)pkt)-> frame_control) || ieee80211_is_auth(((struct ieee80211_mgmt *)pkt)->frame_control) || ieee80211_is_disassoc(((struct ieee80211_mgmt *) pkt)-> frame_control))) { if ((priv-> auth_flag & HOST_MLME_AUTH_PENDING) && ieee80211_is_auth(((struct ieee80211_mgmt *)pkt)-> frame_control)) { priv->auth_flag &= ~HOST_MLME_AUTH_PENDING; priv->auth_flag |= HOST_MLME_AUTH_DONE; priv->host_mlme_wait_condition = MTRUE; wake_up(&priv-> host_mlme_wait_q); } else { PRINTM(MEVENT, "HostMlme: Receive deauth/disassociate\n"); woal_cfg80211_mgmt_frame_register (priv->wdev->wiphy, priv->wdev, IEEE80211_STYPE_DEAUTH, MFALSE); woal_cfg80211_mgmt_frame_register (priv->wdev->wiphy, priv->wdev, IEEE80211_STYPE_DISASSOC, MFALSE); priv->host_mlme = MFALSE; priv->auth_flag = 0; } #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) cfg80211_rx_mlme_mgmt(priv->netdev, pkt, pmevent-> event_len - sizeof(pmevent-> event_id) - MLAN_MAC_ADDR_LENGTH); #else if (ieee80211_is_deauth (((struct ieee80211_mgmt *)pkt)-> frame_control)) cfg80211_send_deauth(priv-> netdev, pkt, pmevent-> event_len - sizeof (pmevent-> event_id) - MLAN_MAC_ADDR_LENGTH); else if (ieee80211_is_auth (((struct ieee80211_mgmt *) pkt)->frame_control)) cfg80211_send_rx_auth(priv-> netdev, pkt, pmevent-> event_len - sizeof (pmevent-> event_id) - MLAN_MAC_ADDR_LENGTH); else if (ieee80211_is_disassoc (((struct ieee80211_mgmt *) pkt)->frame_control)) cfg80211_send_disassoc(priv-> netdev, pkt, pmevent-> event_len - sizeof (pmevent-> event_id) - MLAN_MAC_ADDR_LENGTH); #endif } else #endif #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) cfg80211_rx_mgmt( #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) priv->wdev, #else priv->netdev, #endif freq, 0, ((const t_u8 *) pmevent-> event_buf) + sizeof(pmevent-> event_id), pmevent-> event_len - sizeof(pmevent-> event_id) - MLAN_MAC_ADDR_LENGTH #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) , 0 #endif #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 18, 0) , GFP_ATOMIC #endif ); #else cfg80211_rx_mgmt(priv->netdev, freq, ((const t_u8 *) pmevent->event_buf) + sizeof(pmevent-> event_id), pmevent->event_len - sizeof(pmevent-> event_id) - MLAN_MAC_ADDR_LENGTH, GFP_ATOMIC); #endif } #endif /* KERNEL_VERSION */ } #endif /* STA_CFG80211 || UAP_CFG80211 */ break; #endif /* UAP_SUPPORT */ case MLAN_EVENT_ID_DRV_PASSTHRU: woal_broadcast_event(priv, pmevent->event_buf, pmevent->event_len); break; case MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT: PRINTM(MINFO, "Assoc result\n"); if (priv->media_connected) { PRINTM(MINFO, "Assoc_Rpt: Media Connected\n"); if (!netif_carrier_ok(priv->netdev)) { PRINTM(MINFO, "Assoc_Rpt: Carrier On\n"); netif_carrier_on(priv->netdev); } PRINTM(MINFO, "Assoc_Rpt: Queue Start\n"); woal_wake_queue(priv->netdev); } break; case MLAN_EVENT_ID_DRV_MEAS_REPORT: /* We have received measurement report, wakeup measurement wait queue */ PRINTM(MINFO, "Measurement Report\n"); /* Going out of CAC checking period */ if (priv->phandle->cac_period == MTRUE) { priv->phandle->cac_period = MFALSE; if (priv->phandle->meas_wait_q_woken == MFALSE) { priv->phandle->meas_wait_q_woken = MTRUE; wake_up_interruptible(&priv->phandle-> meas_wait_q); } /* Execute delayed BSS START command */ if (priv->phandle->delay_bss_start == MTRUE) { mlan_ioctl_req *req = NULL; mlan_ds_bss *bss = NULL; /* Clear flag */ priv->phandle->delay_bss_start = MFALSE; PRINTM(MMSG, "Now CAC measure period end. Execute delayed BSS Start command.\n"); req = woal_alloc_mlan_ioctl_req(sizeof (mlan_ds_bss)); if (!req) { PRINTM(MERROR, "Failed to allocate ioctl request buffer\n"); goto done; } bss = (mlan_ds_bss *)req->pbuf; req->req_id = MLAN_IOCTL_BSS; req->action = MLAN_ACT_SET; bss->sub_command = MLAN_OID_BSS_START; memcpy(&bss->param.ssid_bssid, &priv->phandle->delay_ssid_bssid, sizeof(mlan_ssid_bssid)); if (woal_request_ioctl(priv, req, MOAL_NO_WAIT) != MLAN_STATUS_PENDING) { PRINTM(MERROR, "Delayed BSS Start operation failed!\n"); kfree(req); } PRINTM(MMSG, "BSS START Complete!\n"); } #ifdef UAP_SUPPORT #if defined(STA_CFG80211) || defined(UAP_CFG80211) #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) if (priv->uap_host_based && dfs_offload) woal_cfg80211_dfs_vendor_event(priv, event_dfs_cac_finished, &priv->chan); #endif #endif #endif } break; case MLAN_EVENT_ID_DRV_TDLS_TEARDOWN_REQ: #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { tdls_tear_down_event *tdls_event = (tdls_tear_down_event *)pmevent->event_buf; cfg80211_tdls_oper_request(priv->netdev, tdls_event->peer_mac_addr, NL80211_TDLS_TEARDOWN, tdls_event->reason_code, GFP_KERNEL); } #endif #endif break; case MLAN_EVENT_ID_FW_TX_STATUS: { unsigned long flag; tx_status_event *tx_status = (tx_status_event *)(pmevent->event_buf + 4); struct tx_status_info *tx_info = NULL; t_u8 catagory = 0, action = 0, dialog_token = 0; t_u8 peer_addr[6] = { 0 }; confirm_timestamps tsbuff = { 0 }; PRINTM(MINFO, "Receive Tx status: tx_token=%d, pkt_type=0x%x, status=%d tx_seq_num=%d\n", tx_status->tx_token_id, tx_status->packet_type, tx_status->status, priv->tx_seq_num); spin_lock_irqsave(&priv->tx_stat_lock, flag); tx_info = woal_get_tx_info(priv, tx_status->tx_token_id); if (tx_info) { bool ack; struct sk_buff *skb = (struct sk_buff *)tx_info->tx_skb; list_del(&tx_info->link); spin_unlock_irqrestore(&priv->tx_stat_lock, flag); if (!tx_status->status) ack = true; else ack = false; if (priv->phandle->remain_on_channel && tx_info->cancel_remain_on_channel) { remain_priv = priv->phandle->priv[priv-> phandle-> remain_bss_index]; if (remain_priv) { woal_cfg80211_remain_on_channel_cfg (remain_priv, MOAL_NO_WAIT, MTRUE, &channel_status, NULL, 0, 0); priv->phandle-> remain_on_channel = MFALSE; } } { catagory = ((struct ieee80211_mgmt *)skb-> data)->u.action.category; action = *(& ((struct ieee80211_mgmt *) skb->data)->u.action. category + 1); dialog_token = *(&((struct ieee80211_mgmt *)skb->data)->u.action.category + 2); //dt is 2 bytes after category memcpy(peer_addr, (t_u8 *)((struct ieee80211_mgmt *) skb->data)->da, 6); PRINTM(MEVENT, "Wlan: Tx status=%d, cat = %d, act = %d, dt = %d\n", ack, catagory, action, dialog_token); /* this is a tx done for timining measurement action frame * so we need to send the tx timestamp of txed frame back to supplicant * for this send the timestamps along with the tx_status event buffer */ if (catagory == IEEE_MGMT_ACTION_CATEGORY_UNPROTECT_WNM && action == 0x1) { /* create a timestamp buffer to send to host supplicant */ PRINTM(MEVENT, "Wlan: Tx t1=%lld, t4 = %lld, t1_error = %lld, t4_error=%lld\n", tx_status->t1_tstamp, tx_status->t4_tstamp, tx_status->t1_error, tx_status->t4_error); /* for timestamps only use lower 32-bits as spec defines 11v timestamps as 32-bits */ tsbuff.t4 = (u32) woal_le32_to_cpu (tx_status->t4_tstamp); tsbuff.t1 = (u32) woal_le32_to_cpu (tx_status->t1_tstamp); tsbuff.t4_error = (u8)tx_status->t4_error; tsbuff.t1_error = (u8)tx_status->t4_error; tsbuff.egress_time = (u64) woal_le64_to_cpu (tx_status-> egress_time); if (skb_tailroom(skb) < sizeof(confirm_timestamps)) { struct sk_buff *new_skb = NULL; PRINTM(MWARN, "Tx Status: Insufficient skb tailroom %d\n", skb_tailroom (skb)); /* Insufficient skb tailroom - allocate a new skb */ new_skb = skb_copy_expand (skb, 0, sizeof (confirm_timestamps), GFP_ATOMIC); if (unlikely(!new_skb)) { PRINTM(MERROR, "Tx Status: Cannot allocate skb\n"); dev_kfree_skb_any (skb); goto done; } skb = new_skb; PRINTM(MINFO, "new skb tailroom %d\n", skb_tailroom (skb)); } if (tx_info->tx_cookie) { memcpy(skb_put (skb, sizeof (confirm_timestamps)), &tsbuff, sizeof (confirm_timestamps)); } else { sprintf(pevent, "%s " FULL_MACSTR " %u %u %u %u %u %u %llu", CUS_EVT_TIMING_MSMT_CONFIRM, FULL_MAC2STR (peer_addr), dialog_token, tsbuff.t1, tsbuff.t1_error, tsbuff.t4, tsbuff.t4_error, tsbuff.t1 * 10, tsbuff. egress_time); woal_broadcast_event (priv, pevent, strlen (pevent)); } } } #if defined(STA_CFG80211) || defined(UAP_CFG80211) if (tx_info->tx_cookie) { #if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) cfg80211_mgmt_tx_status(priv->netdev, tx_info-> tx_cookie, skb->data, skb->len, ack, GFP_ATOMIC); #else cfg80211_mgmt_tx_status(priv->wdev, tx_info-> tx_cookie, skb->data, skb->len, ack, GFP_ATOMIC); #endif #endif } #endif dev_kfree_skb_any(skb); kfree(tx_info); } else spin_unlock_irqrestore(&priv->tx_stat_lock, flag); } break; case MLAN_EVENT_ID_DRV_FT_RESPONSE: if (priv->phandle->fw_roam_enable) break; #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,10,0) #ifdef STA_CFG80211 if (IS_STA_CFG80211(cfg80211_wext)) { struct cfg80211_ft_event_params ft_event; if (priv->ft_pre_connect) break; memset(&ft_event, 0, sizeof(struct cfg80211_ft_event_params)); PRINTM(MMSG, "wlan : FT response target AP " MACSTR "\n", MAC2STR((t_u8 *)pmevent->event_buf)); DBG_HEXDUMP(MDAT_D, "FT-event ", pmevent->event_buf, pmevent->event_len); memcpy(priv->target_ap_bssid, pmevent->event_buf, ETH_ALEN); ft_event.target_ap = priv->target_ap_bssid; ft_event.ies = pmevent->event_buf + ETH_ALEN; ft_event.ies_len = pmevent->event_len - ETH_ALEN; /*TSPEC info is needed by RIC, However the TS operation is configured by mlanutl */ /*So do not add RIC temporally */ /*when add RIC, 1. query TS status, 2. copy tspec from addts command */ ft_event.ric_ies = NULL; ft_event.ric_ies_len = 0; cfg80211_ft_event(priv->netdev, &ft_event); priv->ft_pre_connect = MTRUE; if (priv->ft_roaming_triggered_by_driver || !(priv->ft_cap & MBIT(0))) { priv->ft_wait_condition = MTRUE; wake_up(&priv->ft_wait_q); } } #endif #endif break; case MLAN_EVENT_ID_FW_ROAM_OFFLOAD_RESULT: #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) woal_cfg80211_vendor_event(priv, event_set_key_mgmt_offload, &enable, sizeof(enable)); #endif #endif memcpy(priv->cfg_bssid, pmevent->event_buf, ETH_ALEN); tlv = (MrvlIEtypesHeader_t *)((t_u8 *)pmevent->event_buf + MLAN_MAC_ADDR_LENGTH); tlv_buf_left = pmevent->event_len - MLAN_MAC_ADDR_LENGTH; while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { tlv_type = woal_le16_to_cpu(tlv->type); tlv_len = woal_le16_to_cpu(tlv->len); if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { PRINTM(MERROR, "Error processing firmware roam success TLVs, bytes left < TLV length\n"); break; } switch (tlv_type) { case TLV_TYPE_APINFO: pinfo = (apinfo *) tlv; break; case TLV_TYPE_ASSOC_REQ_IE: req_tlv = (apinfo *) tlv; break; default: break; } tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + sizeof (MrvlIEtypesHeader_t)); } if (!pinfo) { PRINTM(MERROR, "ERROR:AP info in roaming event buffer is NULL\n"); goto done; } if (req_tlv) { req_ie = req_tlv->rsp_ie; ie_len = req_tlv->header.len; } woal_inform_bss_from_scan_result(priv, NULL, MOAL_NO_WAIT); #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) memset(&roam_info, 0, sizeof(struct cfg80211_roam_info)); roam_info.bssid = priv->cfg_bssid; roam_info.req_ie = req_ie; roam_info.req_ie_len = ie_len; roam_info.resp_ie = pinfo->rsp_ie; roam_info.resp_ie_len = pinfo->header.len; cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL); #else #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, req_ie, ie_len, pinfo->rsp_ie, pinfo->header.len, GFP_KERNEL); #else cfg80211_roamed(priv->netdev, priv->cfg_bssid, req_ie, ie_len, pinfo->rsp_ie, pinfo->header.len, GFP_KERNEL); #endif #endif #ifdef STA_CFG80211 #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) woal_roam_ap_info(priv, pmevent->event_buf, pmevent->event_len); #endif #endif PRINTM(MMSG, "FW Roamed to bssid " MACSTR " successfully\n", MAC2STR(priv->cfg_bssid)); break; default: break; } done: kfree(pevent); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function prints the debug message in mlan * * @param pmoal_handle Pointer to the MOAL context * @param level debug level * @param pformat point to string format buf * * @return N/A */ t_void moal_print(IN t_void *pmoal_handle, IN t_u32 level, IN char *pformat, IN ...) { #ifdef DEBUG_LEVEL1 va_list args; if (level & MHEX_DUMP) { t_u8 *buf = NULL; int len = 0; va_start(args, pformat); buf = (t_u8 *)va_arg(args, t_u8 *); len = (int)va_arg(args, int); va_end(args); #ifdef DEBUG_LEVEL2 if (level & MINFO) HEXDUMP((char *)pformat, buf, len); else #endif /* DEBUG_LEVEL2 */ { if (level & MERROR) DBG_HEXDUMP(MERROR, (char *)pformat, buf, len); if (level & MCMD_D) DBG_HEXDUMP(MCMD_D, (char *)pformat, buf, len); if (level & MDAT_D) DBG_HEXDUMP(MDAT_D, (char *)pformat, buf, len); if (level & MIF_D) DBG_HEXDUMP(MIF_D, (char *)pformat, buf, len); if (level & MFW_D) DBG_HEXDUMP(MFW_D, (char *)pformat, buf, len); if (level & MEVT_D) DBG_HEXDUMP(MEVT_D, (char *)pformat, buf, len); } } else { if (drvdbg & level) { va_start(args, pformat); vprintk(pformat, args); va_end(args); } } #endif /* DEBUG_LEVEL1 */ } /** * @brief This function prints the network interface name * * @param pmoal_handle Pointer to the MOAL context * @param bss_index BSS index * @param level debug level * * @return N/A */ t_void moal_print_netintf(IN t_void *pmoal_handle, IN t_u32 bss_index, IN t_u32 level) { #ifdef DEBUG_LEVEL1 moal_handle *phandle = (moal_handle *)pmoal_handle; if (phandle) { if ((bss_index < MLAN_MAX_BSS_NUM) && phandle->priv[bss_index] && phandle->priv[bss_index]->netdev) { if (drvdbg & level) printk("%s: ", phandle->priv[bss_index]->netdev->name); } } #endif /* DEBUG_LEVEL1 */ } /** * @brief This function asserts the existence of the passed argument * * @param pmoal_handle A pointer to moal_private structure * @param cond Condition to check * * @return N/A */ t_void moal_assert(IN t_void *pmoal_handle, IN t_u32 cond) { if (!cond) { panic("Assert failed: Panic!"); } } /** * @brief This function save the histogram data * * @param pmoal_handle A pointer to moal_private structure * @param bss_index BSS index * @param rx_rate rx rate index * @param snr snr * @param nflr noise floor * @param antenna antenna * * @return N/A */ t_void moal_hist_data_add(IN t_void *pmoal_handle, IN t_u32 bss_index, IN t_u8 rx_rate, IN t_s8 snr, IN t_s8 nflr, IN t_u8 antenna) { moal_private *priv = NULL; priv = woal_bss_index_to_priv(pmoal_handle, bss_index); if (priv && antenna >= priv->phandle->histogram_table_num) antenna = 0; if (priv && priv->hist_data[antenna]) woal_hist_data_add(priv, rx_rate, snr, nflr, antenna); } /** * @brief This function update the peer signal * * @param pmoal_handle A pointer to moal_private structure * @param bss_index BSS index * @param peer_addr peer address * @param snr snr * @param nflr noise floor * * @return N/A */ t_void moal_updata_peer_signal(IN t_void *pmoal_handle, IN t_u32 bss_index, IN t_u8 *peer_addr, IN t_s8 snr, IN t_s8 nflr) { moal_private *priv = NULL; struct tdls_peer *peer = NULL; unsigned long flags; priv = woal_bss_index_to_priv(pmoal_handle, bss_index); if (priv && priv->enable_auto_tdls) { spin_lock_irqsave(&priv->tdls_lock, flags); list_for_each_entry(peer, &priv->tdls_list, link) { if (!memcmp(peer->peer_addr, peer_addr, ETH_ALEN)) { peer->rssi = nflr - snr; peer->rssi_jiffies = jiffies; break; } } spin_unlock_irqrestore(&priv->tdls_lock, flags); } } /** * @brief This function records host time in nano seconds * * @return 64 bit value of host time in nano seconds */ s64 get_host_time_ns(void) { struct timespec ts; getnstimeofday(&ts); return timespec_to_ns(&ts); } /** * @brief Retrieves the current system time * * @param time Pointer for the seconds of system time * * @return MLAN_STATUS_SUCCESS */ mlan_status moal_get_host_time_ns(OUT t_u64 *time) { struct timespec ts; t_u64 hclk_val; getnstimeofday(&ts); hclk_val = (ts.tv_sec * 1000000000L) + ts.tv_nsec; *time = hclk_val; return MLAN_STATUS_SUCCESS; } /** * @brief Performs division of 64-bit num with base * @brief do_div does two things * @brief 1. modifies the 64-bit num in place with * @brief the quotient, i.e., num becomes quotient * @brief 2. do_div() returns the 32-bit reminder * * @param num dividend * @param base divisor * @return returns 64-bit quotient */ t_u64 moal_do_div(IN t_u64 num, IN t_u32 base) { t_u64 val = num; do_div(val, base); return val; } #if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) /** * @brief Performs wait event * * @param pmoal_handle t_void * @param bss_index index of priv * @return MLAN_STATUS_SUCCESS */ mlan_status moal_wait_hostcmd_complete(IN t_void *pmoal_handle, IN t_u32 bss_index) { mlan_status status = MLAN_STATUS_SUCCESS; moal_handle *handle = (moal_handle *)pmoal_handle; moal_private *priv = woal_bss_index_to_priv(handle, bss_index); long time_left = 0; ENTER(); if (!priv) { PRINTM(MERROR, "moal_wait_event: priv is null!\n"); goto done; } priv->hostcmd_wait_condition = MFALSE; time_left = wait_event_timeout(priv->hostcmd_wait_q, priv->hostcmd_wait_condition, MOAL_IOCTL_TIMEOUT); if (!time_left) { PRINTM(MERROR, "moal_wait_event: wait timeout "); status = MLAN_STATUS_FAILURE; } done: LEAVE(); return status; } /** * @brief wake up esa wait_q * * @param pmoal_handle t_void * @param bss_index index of priv * @return MLAN_STATUS_SUCCESS */ mlan_status moal_notify_hostcmd_complete(IN t_void *pmoal_handle, IN t_u32 bss_index) { mlan_status status = MLAN_STATUS_SUCCESS; moal_handle *handle = (moal_handle *)pmoal_handle; moal_private *priv = woal_bss_index_to_priv(handle, bss_index); ENTER(); priv->hostcmd_wait_condition = MTRUE; wake_up(&priv->hostcmd_wait_q); LEAVE(); return status; } #endif