/* * Copyright (C) 2011-2014 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 the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program. * If not, see . */ /* ** Id: //Department/DaVinci/BRANCHES/MT6620_WIFI_DRIVER_V2_3/os/linux/gl_init.c#7 */ /*! \file gl_init.c \brief Main routines of Linux driver This file contains the main routines of Linux driver for MediaTek Inc. 802.11 Wireless LAN Adapters. */ /* ** Log: gl_init.c ** ** 09 03 2013 cp.wu ** add path for reassociation * * 07 17 2012 yuche.tsai * NULL * Fix compile error. * * 07 17 2012 yuche.tsai * NULL * Fix compile error for JB. * * 07 17 2012 yuche.tsai * NULL * Let netdev bring up. * * 07 17 2012 yuche.tsai * NULL * Compile no error before trial run. * * 06 13 2012 yuche.tsai * NULL * Update maintrunk driver. * Add support for driver compose assoc request frame. * * 05 25 2012 yuche.tsai * NULL * Fix reset KE issue. * * 05 11 2012 cp.wu * [WCXRP00001237] [MT6620 Wi-Fi][Driver] Show MAC address and MAC address source for ACS's convenience * show MAC address & source while initiliazation * * 03 02 2012 terry.wu * NULL * EXPORT_SYMBOL(rsnParseCheckForWFAInfoElem);. * * 03 02 2012 terry.wu * NULL * Snc CFG80211 modification for ICS migration from branch 2.2. * * 03 02 2012 terry.wu * NULL * Sync CFG80211 modification from branch 2,2. * * 03 02 2012 terry.wu * NULL * Enable CFG80211 Support. * * 12 22 2011 george.huang * [WCXRP00000905] [MT6628 Wi-Fi][FW] Code refinement for ROM/ RAM module dependency * using global variable instead of stack for setting wlanoidSetNetworkAddress(), due to buffer may be released before * TX thread handling * * 11 18 2011 yuche.tsai * NULL * CONFIG P2P support RSSI query, default turned off. * * 11 14 2011 yuche.tsai * [WCXRP00001107] [Volunteer Patch][Driver] Large Network Type index assert in FW issue. * Fix large network type index assert in FW issue. * * 11 14 2011 cm.chang * NULL * Fix compiling warning * * 11 11 2011 yuche.tsai * NULL * Fix work thread cancel issue. * * 11 10 2011 cp.wu * [WCXRP00001098] [MT6620 Wi-Fi][Driver] Replace printk by DBG LOG macros in linux porting layer * 1. eliminaite direct calls to printk in porting layer. * 2. replaced by DBGLOG, which would be XLOG on ALPS platforms. * * 10 06 2011 eddie.chen * [WCXRP00001027] [MT6628 Wi-Fi][Firmware/Driver] Tx fragmentation * Add rlmDomainGetChnlList symbol. * * 09 22 2011 cm.chang * NULL * Safer writng stype to avoid unitialized regitry structure * * 09 21 2011 cm.chang * [WCXRP00000969] [MT6620 Wi-Fi][Driver][FW] Channel list for 5G band based on country code * Avoid possible structure alignment problem * * 09 20 2011 chinglan.wang * [WCXRP00000989] [WiFi Direct] [Driver] Add a new io control API to start the formation for the sigma test. * . * * 09 08 2011 cm.chang * [WCXRP00000969] [MT6620 Wi-Fi][Driver][FW] Channel list for 5G band based on country code * Use new fields ucChannelListMap and ucChannelListIndex in NVRAM * * 08 31 2011 cm.chang * [WCXRP00000969] [MT6620 Wi-Fi][Driver][FW] Channel list for 5G band based on country code * . * * 08 11 2011 cp.wu * [WCXRP00000830] [MT6620 Wi-Fi][Firmware] Use MDRDY counter to detect empty channel for shortening scan time * expose scnQuerySparseChannel() for P2P-FSM. * * 08 11 2011 cp.wu * [WCXRP00000830] [MT6620 Wi-Fi][Firmware] Use MDRDY counter to detect empty channel for shortening scan time * sparse channel detection: * driver: collect sparse channel information with scan-done event * * 08 02 2011 yuche.tsai * [WCXRP00000896] [Volunteer Patch][WiFi Direct][Driver] GO with multiple client, TX deauth to a disconnecting * device issue. * Fix GO send deauth frame issue. * * 07 07 2011 wh.su * [WCXRP00000839] [MT6620 Wi-Fi][Driver] Add the dumpMemory8 and dumpMemory32 EXPORT_SYMBOL * Add the dumpMemory8 symbol export for debug mode. * * 07 06 2011 terry.wu * [WCXRP00000735] [MT6620 Wi-Fi][BoW][FW/Driver] Protect BoW connection establishment * Improve BoW connection establishment speed. * * 07 05 2011 yuche.tsai * [WCXRP00000821] [Volunteer Patch][WiFi Direct][Driver] WiFi Direct Connection Speed Issue * Export one symbol for enhancement. * * 06 13 2011 eddie.chen * [WCXRP00000779] [MT6620 Wi-Fi][DRV] Add tx rx statistics in linux and use netif_rx_ni * Add tx rx statistics and netif_rx_ni. * * 05 27 2011 cp.wu * [WCXRP00000749] [MT6620 Wi-Fi][Driver] Add band edge tx power control to Wi-Fi NVRAM * invoke CMD_ID_SET_EDGE_TXPWR_LIMIT when there is valid data exist in NVRAM content. * * 05 18 2011 cp.wu * [WCXRP00000734] [MT6620 Wi-Fi][Driver] Pass PHY_PARAM in NVRAM to firmware domain * pass PHY_PARAM in NVRAM from driver to firmware. * * 05 09 2011 jeffrey.chang * [WCXRP00000710] [MT6620 Wi-Fi] Support pattern filter update function on IP address change * support ARP filter through kernel notifier * * 05 03 2011 chinghwa.yu * [WCXRP00000065] Update BoW design and settings * Use kalMemAlloc to allocate event buffer for kalIndicateBOWEvent. * * 04 27 2011 george.huang * [WCXRP00000684] [MT6620 Wi-Fi][Driver] Support P2P setting ARP filter * Support P2P ARP filter setting on early suspend/ late resume * * 04 18 2011 terry.wu * [WCXRP00000660] [MT6620 Wi-Fi][Driver] Remove flag CFG_WIFI_DIRECT_MOVED * Remove flag CFG_WIFI_DIRECT_MOVED. * * 04 15 2011 chinghwa.yu * [WCXRP00000065] Update BoW design and settings * Add BOW short range mode. * * 04 14 2011 yuche.tsai * [WCXRP00000646] [Volunteer Patch][MT6620][FW/Driver] Sigma Test Modification for some test case. * Modify some driver connection flow or behavior to pass Sigma test more easier.. * * 04 12 2011 cm.chang * [WCXRP00000634] [MT6620 Wi-Fi][Driver][FW] 2nd BSS will not support 40MHz bandwidth for concurrency * . * * 04 11 2011 george.huang * [WCXRP00000621] [MT6620 Wi-Fi][Driver] Support P2P supplicant to set power mode * export wlan functions to p2p * * 04 08 2011 pat.lu * [WCXRP00000623] [MT6620 Wi-Fi][Driver] use ARCH define to distinguish PC Linux driver * Use CONFIG_X86 instead of PC_LINUX_DRIVER_USE option to have proper compile setting for PC Linux driver * * 04 08 2011 cp.wu * [WCXRP00000540] [MT5931][Driver] Add eHPI8/eHPI16 support to Linux Glue Layer * glBusFreeIrq() should use the same pvCookie as glBusSetIrq() or request_irq()/free_irq() won't work as a pair. * * 04 08 2011 eddie.chen * [WCXRP00000617] [MT6620 Wi-Fi][DRV/FW] Fix for sigma * Fix for sigma * * 04 06 2011 cp.wu * [WCXRP00000540] [MT5931][Driver] Add eHPI8/eHPI16 support to Linux Glue Layer * 1. do not check for pvData inside wlanNetCreate() due to it is NULL for eHPI port * 2. update perm_addr as well for MAC address * 3. not calling check_mem_region() anymore for eHPI * 4. correct MSC_CS macro for 0-based notation * * 03 29 2011 cp.wu * [WCXRP00000598] [MT6620 Wi-Fi][Driver] Implementation of interface for communicating with user space process for * RESET_START and RESET_END events * fix typo. * * 03 29 2011 cp.wu * [WCXRP00000598] [MT6620 Wi-Fi][Driver] Implementation of interface for communicating with user space process for * RESET_START and RESET_END events * implement kernel-to-userspace communication via generic netlink socket for whole-chip resetting mechanism * * 03 23 2011 cp.wu * [WCXRP00000540] [MT5931][Driver] Add eHPI8/eHPI16 support to Linux Glue Layer * apply multi-queue operation only for linux kernel > 2.6.26 * * 03 22 2011 pat.lu * [WCXRP00000592] [MT6620 Wi-Fi][Driver] Support PC Linux Environment Driver Build * Add a compiler option "PC_LINUX_DRIVER_USE" for building driver in PC Linux environment. * * 03 21 2011 cp.wu * [WCXRP00000540] [MT5931][Driver] Add eHPI8/eHPI16 support to Linux Glue Layer * portability for compatible with linux 2.6.12. * * 03 21 2011 cp.wu * [WCXRP00000540] [MT5931][Driver] Add eHPI8/eHPI16 support to Linux Glue Layer * improve portability for awareness of early version of linux kernel and wireless extension. * * 03 21 2011 cp.wu * [WCXRP00000540] [MT5931][Driver] Add eHPI8/eHPI16 support to Linux Glue Layer * portability improvement * * 03 18 2011 jeffrey.chang * [WCXRP00000512] [MT6620 Wi-Fi][Driver] modify the net device relative functions to support the H/W multiple queue * remove early suspend functions * * 03 17 2011 cp.wu * [WCXRP00000562] [MT6620 Wi-Fi][Driver] I/O buffer pre-allocation to avoid physically continuous memory shortage * after system running for a long period * reverse order to prevent probing racing. * * 03 16 2011 cp.wu * [WCXRP00000562] [MT6620 Wi-Fi][Driver] I/O buffer pre-allocation to avoid physically continuous memory shortage * after system running for a long period * 1. pre-allocate physical continuous buffer while module is being loaded * 2. use pre-allocated physical continuous buffer for TX/RX DMA transfer * * The windows part remained the same as before, but added similar APIs to hide the difference. * * 03 15 2011 jeffrey.chang * [WCXRP00000558] [MT6620 Wi-Fi][MT6620 Wi-Fi][Driver] refine the queue selection algorithm for WMM * refine the queue_select function * * 03 10 2011 cp.wu * [WCXRP00000532] [MT6620 Wi-Fi][Driver] Migrate NVRAM configuration procedures from MT6620 E2 to MT6620 E3 * deprecate configuration used by MT6620 E2 * * 03 10 2011 terry.wu * [WCXRP00000505] [MT6620 Wi-Fi][Driver/FW] WiFi Direct Integration * Remove unnecessary assert and message. * * 03 08 2011 terry.wu * [WCXRP00000505] [MT6620 Wi-Fi][Driver/FW] WiFi Direct Integration * Export nicQmUpdateWmmParms. * * 03 03 2011 jeffrey.chang * [WCXRP00000512] [MT6620 Wi-Fi][Driver] modify the net device relative functions to support the H/W multiple queue * support concurrent network * * 03 03 2011 jeffrey.chang * [WCXRP00000512] [MT6620 Wi-Fi][Driver] modify the net device relative functions to support the H/W multiple queue * modify net device relative functions to support multiple H/W queues * * 02 24 2011 george.huang * [WCXRP00000495] [MT6620 Wi-Fi][FW] Support pattern filter for unwanted ARP frames * Support ARP filter during suspended * * 02 21 2011 cp.wu * [WCXRP00000482] [MT6620 Wi-Fi][Driver] Simplify logic for checking NVRAM existence in driver domain * simplify logic for checking NVRAM existence only once. * * 02 17 2011 terry.wu * [WCXRP00000459] [MT6620 Wi-Fi][Driver] Fix deference null pointer problem in wlanRemove * Fix deference a null pointer problem in wlanRemove. * * 02 16 2011 jeffrey.chang * NULL * fix compilig error * * 02 16 2011 jeffrey.chang * NULL * Add query ipv4 and ipv6 address during early suspend and late resume * * 02 15 2011 jeffrey.chang * NULL * to support early suspend in android * * 02 11 2011 yuche.tsai * [WCXRP00000431] [Volunteer Patch][MT6620][Driver] Add MLME support for deauthentication under AP(Hot-Spot) mode. * Add one more export symbol. * * 02 10 2011 yuche.tsai * [WCXRP00000431] [Volunteer Patch][MT6620][Driver] Add MLME support for deauthentication under AP(Hot-Spot) mode. * Add RX deauthentication & disassociation process under Hot-Spot mode. * * 02 09 2011 terry.wu * [WCXRP00000383] [MT6620 Wi-Fi][Driver] Separate WiFi and P2P driver into two modules * Halt p2p module init and exit until TxThread finished p2p register and unregister. * * 02 08 2011 george.huang * [WCXRP00000422] [MT6620 Wi-Fi][Driver] support query power mode OID handler * Support querying power mode OID. * * 02 08 2011 yuche.tsai * [WCXRP00000421] [Volunteer Patch][MT6620][Driver] Fix incorrect SSID length Issue * Export Deactivation Network. * * 02 01 2011 jeffrey.chang * [WCXRP00000414] KAL Timer is not unregistered when driver not loaded * Unregister the KAL timer during driver unloading * * 01 26 2011 cm.chang * [WCXRP00000395] [MT6620 Wi-Fi][Driver][FW] Search STA_REC with additional net type index argument * Allocate system RAM if fixed message or mgmt buffer is not available * * 01 19 2011 cp.wu * [WCXRP00000371] [MT6620 Wi-Fi][Driver] make linux glue layer portable for Android 2.3.1 with Linux 2.6.35.7 * add compile option to check linux version 2.6.35 for different usage of system API to improve portability * * 01 12 2011 cp.wu * [WCXRP00000357] [MT6620 Wi-Fi][Driver][Bluetooth over Wi-Fi] add another net device interface for BT AMP * implementation of separate BT_OVER_WIFI data path. * * 01 10 2011 cp.wu * [WCXRP00000349] [MT6620 Wi-Fi][Driver] make kalIoctl() of linux port as a thread safe API to avoid potential issues * due to multiple access * use mutex to protect kalIoctl() for thread safe. * * 01 04 2011 cp.wu * [WCXRP00000338] [MT6620 Wi-Fi][Driver] Separate kalMemAlloc into kmalloc and vmalloc implementations to ease * physically continuous memory demands * separate kalMemAlloc() into virtually-continuous and physically-continuous type to ease slab system pressure * * 12 15 2010 cp.wu * [WCXRP00000265] [MT6620 Wi-Fi][Driver] Remove set_mac_address routine from legacy Wi-Fi Android driver * remove set MAC address. MAC address is always loaded from NVRAM instead. * * 12 10 2010 kevin.huang * [WCXRP00000128] [MT6620 Wi-Fi][Driver] Add proc support to Android Driver for debug and driver status check * Add Linux Proc Support * * 11 01 2010 yarco.yang * [WCXRP00000149] [MT6620 WI-Fi][Driver]Fine tune performance on MT6516 platform * Add GPIO debug function * * 11 01 2010 cp.wu * [WCXRP00000056] [MT6620 Wi-Fi][Driver] NVRAM implementation with Version Check[WCXRP00000150] [MT6620 Wi-Fi][Driver] * Add implementation for querying current TX rate from firmware auto rate module * 1) Query link speed (TX rate) from firmware directly with buffering mechanism to reduce overhead * 2) Remove CNM CH-RECOVER event handling * 3) cfg read/write API renamed with kal prefix for unified naming rules. * * 10 26 2010 cp.wu * [WCXRP00000056] [MT6620 Wi-Fi][Driver] NVRAM implementation with Version Check[WCXRP00000137] [MT6620 Wi-Fi] [FW] * Support NIC capability query command * 1) update NVRAM content template to ver 1.02 * 2) add compile option for querying NIC capability (default: off) * 3) modify AIS 5GHz support to run-time option, which could be turned on by registry or NVRAM setting * 4) correct auto-rate compiler error under linux (treat warning as error) * 5) simplify usage of NVRAM and REG_INFO_T * 6) add version checking between driver and firmware * * 10 21 2010 chinghwa.yu * [WCXRP00000065] Update BoW design and settings * . * * 10 19 2010 jeffrey.chang * [WCXRP00000120] [MT6620 Wi-Fi][Driver] Refine linux kernel module to the license of MTK propietary and enable MTK * HIF by default * Refine linux kernel module to the license of MTK and enable MTK HIF * * 10 18 2010 jeffrey.chang * [WCXRP00000106] [MT6620 Wi-Fi][Driver] Enable setting multicast callback in Android * . * * 10 18 2010 cp.wu * [WCXRP00000056] [MT6620 Wi-Fi][Driver] NVRAM implementation with Version Check[WCXRP00000086] [MT6620 Wi-Fi][Driver] * The mac address is all zero at android * complete implementation of Android NVRAM access * * 09 27 2010 chinghwa.yu * [WCXRP00000063] Update BCM CoEx design and settings[WCXRP00000065] Update BoW design and settings * Update BCM/BoW design and settings. * * 09 23 2010 cp.wu * [WCXRP00000051] [MT6620 Wi-Fi][Driver] WHQL test fail in MAC address changed item * use firmware reported mac address right after wlanAdapterStart() as permanent address * * 09 21 2010 kevin.huang * [WCXRP00000052] [MT6620 Wi-Fi][Driver] Eliminate Linux Compile Warning * Eliminate Linux Compile Warning * * 09 03 2010 kevin.huang * NULL * Refine #include sequence and solve recursive/nested #include issue * * 09 01 2010 wh.su * NULL * adding the wapi support for integration test. * * 08 18 2010 yarco.yang * NULL * 1. Fixed HW checksum offload function not work under Linux issue. * 2. Add debug message. * * 08 16 2010 yarco.yang * NULL * Support Linux x86 * * 08 02 2010 jeffrey.chang * NULL * 1) modify tx service thread to avoid busy looping * 2) add spin lock declartion for linux build * * 07 29 2010 jeffrey.chang * NULL * fix memory leak for module unloading * * 07 28 2010 jeffrey.chang * NULL * 1) remove unused spinlocks * 2) enable encyption ioctls * 3) fix scan ioctl which may cause supplicant to hang * * 07 23 2010 jeffrey.chang * * bug fix: allocate regInfo when disabling firmware download * * 07 23 2010 jeffrey.chang * * use glue layer api to decrease or increase counter atomically * * 07 22 2010 jeffrey.chang * * add new spinlock * * 07 19 2010 jeffrey.chang * * modify cmd/data path for new design * * 07 08 2010 cp.wu * * [WPD00003833] [MT6620 and MT5931] Driver migration - move to new repository. * * 06 06 2010 kevin.huang * [WPD00003832][MT6620 5931] Create driver base * [MT6620 5931] Create driver base * * 05 26 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * 1) Modify set mac address code * 2) remove power management macro * * 05 10 2010 cp.wu * [WPD00003831][MT6620 Wi-Fi] Add framework for Wi-Fi Direct support * implement basic wi-fi direct framework * * 05 07 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * prevent supplicant accessing driver during resume * * 05 07 2010 cp.wu * [WPD00003831][MT6620 Wi-Fi] Add framework for Wi-Fi Direct support * add basic framework for implementating P2P driver hook. * * 04 27 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * 1) fix firmware download bug * 2) remove query statistics for acelerating firmware download * * 04 27 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * follow Linux's firmware framework, and remove unused kal API * * 04 21 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * add for private ioctl support * * 04 19 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * Query statistics from firmware * * 04 19 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * modify tcp/ip checksum offload flags * * 04 16 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * fix tcp/ip checksum offload bug * * 04 13 2010 cp.wu * [WPD00003823][MT6620 Wi-Fi] Add Bluetooth-over-Wi-Fi support * add framework for BT-over-Wi-Fi support. * * * * * * * * * * * * * * * * * 1) prPendingCmdInfo is replaced by queue for multiple handler * * * * * * * * * * * * * * * * * capability * * * * * * * * * * * * * * * * * 2) command sequence number is now increased atomically * * * * * * * * * * * * * * * * * 3) private data could be hold and taken use for other purpose * * 04 09 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * fix spinlock usage * * 04 07 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * Set MAC address from firmware * * 04 07 2010 cp.wu * [WPD00001943]Create WiFi test driver framework on WinXP * rWlanInfo should be placed at adapter rather than glue due to most operations * * * * * * are done in adapter layer. * * 04 07 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * (1)improve none-glue code portability * * (2) disable set Multicast address during atomic context * * 04 06 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * adding debug module * * 03 31 2010 wh.su * [WPD00003816][MT6620 Wi-Fi] Adding the security support * modify the wapi related code for new driver's design. * * 03 30 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * emulate NDIS Pending OID facility * * 03 26 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * fix f/w download start and load address by using config.h * * 03 26 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * [WPD00003826] Initial import for Linux port * adding firmware download support * * 03 24 2010 jeffrey.chang * [WPD00003826]Initial import for Linux port * initial import for Linux port ** \main\maintrunk.MT5921\52 2009-10-27 22:49:59 GMT mtk01090 ** Fix compile error for Linux EHPI driver ** \main\maintrunk.MT5921\51 2009-10-20 17:38:22 GMT mtk01090 ** Refine driver unloading and clean up procedure. Block requests, stop main thread and clean up queued requests, ** and then stop hw. ** \main\maintrunk.MT5921\50 2009-10-08 10:33:11 GMT mtk01090 ** Avoid accessing private data of net_device directly. Replace with netdev_priv(). Add more checking for input ** parameters and pointers. ** \main\maintrunk.MT5921\49 2009-09-28 20:19:05 GMT mtk01090 ** Add private ioctl to carry OID structures. Restructure public/private ioctl interfaces to Linux kernel. ** \main\maintrunk.MT5921\48 2009-09-03 13:58:46 GMT mtk01088 ** remove non-used code ** \main\maintrunk.MT5921\47 2009-09-03 11:40:25 GMT mtk01088 ** adding the module parameter for wapi ** \main\maintrunk.MT5921\46 2009-08-18 22:56:41 GMT mtk01090 ** Add Linux SDIO (with mmc core) support. ** Add Linux 2.6.21, 2.6.25, 2.6.26. ** Fix compile warning in Linux. ** \main\maintrunk.MT5921\45 2009-07-06 20:53:00 GMT mtk01088 ** adding the code to check the wapi 1x frame ** \main\maintrunk.MT5921\44 2009-06-23 23:18:55 GMT mtk01090 ** Add build option BUILD_USE_EEPROM and compile option CFG_SUPPORT_EXT_CONFIG for NVRAM support ** \main\maintrunk.MT5921\43 2009-02-16 23:46:51 GMT mtk01461 ** Revise the order of increasing u4TxPendingFrameNum because of CFG_TX_RET_TX_CTRL_EARLY ** \main\maintrunk.MT5921\42 2009-01-22 13:11:59 GMT mtk01088 ** set the tid and 1x value at same packet reserved field ** \main\maintrunk.MT5921\41 2008-10-20 22:43:53 GMT mtk01104 ** Fix wrong variable name "prDev" in wlanStop() ** \main\maintrunk.MT5921\40 2008-10-16 15:37:10 GMT mtk01461 ** add handle WLAN_STATUS_SUCCESS in wlanHardStartXmit() for CFG_TX_RET_TX_CTRL_EARLY ** \main\maintrunk.MT5921\39 2008-09-25 15:56:21 GMT mtk01461 ** Update driver for Code review ** \main\maintrunk.MT5921\38 2008-09-05 17:25:07 GMT mtk01461 ** Update Driver for Code Review ** \main\maintrunk.MT5921\37 2008-09-02 10:57:06 GMT mtk01461 ** Update driver for code review ** \main\maintrunk.MT5921\36 2008-08-05 01:53:28 GMT mtk01461 ** Add support for linux statistics ** \main\maintrunk.MT5921\35 2008-08-04 16:52:58 GMT mtk01461 ** Fix ASSERT if removing module in BG_SSID_SCAN state ** \main\maintrunk.MT5921\34 2008-06-13 22:52:24 GMT mtk01461 ** Revise status code handling in wlanHardStartXmit() for WLAN_STATUS_SUCCESS ** \main\maintrunk.MT5921\33 2008-05-30 18:56:53 GMT mtk01461 ** Not use wlanoidSetCurrentAddrForLinux() ** \main\maintrunk.MT5921\32 2008-05-30 14:39:40 GMT mtk01461 ** Remove WMM Assoc Flag ** \main\maintrunk.MT5921\31 2008-05-23 10:26:40 GMT mtk01084 ** modify wlanISR interface ** \main\maintrunk.MT5921\30 2008-05-03 18:52:36 GMT mtk01461 ** Fix Unset Broadcast filter when setMulticast ** \main\maintrunk.MT5921\29 2008-05-03 15:17:26 GMT mtk01461 ** Move Query Media Status to GLUE ** \main\maintrunk.MT5921\28 2008-04-24 22:48:21 GMT mtk01461 ** Revise set multicast function by using windows oid style for LP own back ** \main\maintrunk.MT5921\27 2008-04-24 12:00:08 GMT mtk01461 ** Fix multicast setting in Linux and add comment ** \main\maintrunk.MT5921\26 2008-03-28 10:40:22 GMT mtk01461 ** Fix set mac address func in Linux ** \main\maintrunk.MT5921\25 2008-03-26 15:37:26 GMT mtk01461 ** Add set MAC Address ** \main\maintrunk.MT5921\24 2008-03-26 14:24:53 GMT mtk01461 ** For Linux, set net_device has feature with checksum offload by default ** \main\maintrunk.MT5921\23 2008-03-11 14:50:52 GMT mtk01461 ** Fix typo ** \main\maintrunk.MT5921\22 2008-02-29 15:35:20 GMT mtk01088 ** add 1x decide code for sw port control ** \main\maintrunk.MT5921\21 2008-02-21 15:01:54 GMT mtk01461 ** Rearrange the set off place of GLUE spin lock in HardStartXmit ** \main\maintrunk.MT5921\20 2008-02-12 23:26:50 GMT mtk01461 ** Add debug option - Packet Order for Linux and add debug level - Event ** \main\maintrunk.MT5921\19 2007-12-11 00:11:12 GMT mtk01461 ** Fix SPIN_LOCK protection ** \main\maintrunk.MT5921\18 2007-11-30 17:02:25 GMT mtk01425 ** 1. Set Rx multicast packets mode before setting the address list ** \main\maintrunk.MT5921\17 2007-11-26 19:44:24 GMT mtk01461 ** Add OS_TIMESTAMP to packet ** \main\maintrunk.MT5921\16 2007-11-21 15:47:20 GMT mtk01088 ** fixed the unload module issue ** \main\maintrunk.MT5921\15 2007-11-07 18:37:38 GMT mtk01461 ** Fix compile warnning ** \main\maintrunk.MT5921\14 2007-11-02 01:03:19 GMT mtk01461 ** Unify TX Path for Normal and IBSS Power Save + IBSS neighbor learning ** \main\maintrunk.MT5921\13 2007-10-30 10:42:33 GMT mtk01425 ** 1. Refine for multicast list ** \main\maintrunk.MT5921\12 2007-10-25 18:08:13 GMT mtk01461 ** Add VOIP SCAN Support & Refine Roaming ** Revision 1.4 2007/07/05 07:25:33 MTK01461 ** Add Linux initial code, modify doc, add 11BB, RF init code ** ** Revision 1.3 2007/06/27 02:18:50 MTK01461 ** Update SCAN_FSM, Initial(Can Load Module), Proc(Can do Reg R/W), TX API ** ** Revision 1.2 2007/06/25 06:16:24 MTK01461 ** Update illustrations, gl_init.c, gl_kal.c, gl_kal.h, gl_os.h and RX API ** */ /******************************************************************************* * C O M P I L E R F L A G S ******************************************************************************** */ /******************************************************************************* * E X T E R N A L R E F E R E N C E S ******************************************************************************** */ #include "gl_os.h" #include "debug.h" #include "wlan_lib.h" #include "gl_wext.h" #include "gl_cfg80211.h" #include "precomp.h" #if CFG_SUPPORT_AGPS_ASSIST #include "gl_kal.h" #endif #if defined(CONFIG_MTK_TC1_FEATURE) #include #endif #include "gl_vendor.h" #ifdef FW_CFG_SUPPORT #include "fwcfg.h" #endif /******************************************************************************* * C O N S T A N T S ******************************************************************************** */ /* #define MAX_IOREQ_NUM 10 */ BOOLEAN fgIsUnderSuspend = false; #if CFG_ENABLE_WIFI_DIRECT spinlock_t g_p2p_lock; int g_u4P2PEnding = 0; int g_u4P2POnOffing = 0; #endif /******************************************************************************* * D A T A T Y P E S ******************************************************************************** */ /* Tasklet mechanism is like buttom-half in Linux. We just want to * send a signal to OS for interrupt defer processing. All resources * are NOT allowed reentry, so txPacket, ISR-DPC and ioctl must avoid preempty. */ typedef struct _WLANDEV_INFO_T { struct net_device *prDev; } WLANDEV_INFO_T, *P_WLANDEV_INFO_T; /******************************************************************************* * P U B L I C D A T A ******************************************************************************** */ MODULE_AUTHOR(NIC_AUTHOR); MODULE_DESCRIPTION(NIC_DESC); MODULE_SUPPORTED_DEVICE(NIC_NAME); MODULE_LICENSE("GPL"); #define NIC_INF_NAME "wlan%d" /* interface name */ #if CFG_TC1_FEATURE #define NIC_INF_NAME_IN_AP_MODE "legacy%d" #endif /* support to change debug module info dynamically */ UINT_8 aucDebugModule[DBG_MODULE_NUM]; UINT_32 u4DebugModule = 0; /* 4 2007/06/26, mikewu, now we don't use this, we just fix the number of wlan device to 1 */ static WLANDEV_INFO_T arWlanDevInfo[CFG_MAX_WLAN_DEVICES] = { {0} }; static UINT_32 u4WlanDevNum; /* How many NICs coexist now */ /**20150205 added work queue for sched_scan to avoid cfg80211 stop schedule scan dead loack**/ struct delayed_work sched_workq; /******************************************************************************* * P R I V A T E D A T A ******************************************************************************** */ #if CFG_ENABLE_WIFI_DIRECT static SUB_MODULE_HANDLER rSubModHandler[SUB_MODULE_NUM] = { {NULL} }; #endif #define CHAN2G(_channel, _freq, _flags) \ { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel mtk_2ghz_channels[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), CHAN2G(3, 2422, 0), CHAN2G(4, 2427, 0), CHAN2G(5, 2432, 0), CHAN2G(6, 2437, 0), CHAN2G(7, 2442, 0), CHAN2G(8, 2447, 0), CHAN2G(9, 2452, 0), CHAN2G(10, 2457, 0), CHAN2G(11, 2462, 0), CHAN2G(12, 2467, 0), CHAN2G(13, 2472, 0), CHAN2G(14, 2484, 0), }; #define CHAN5G(_channel, _flags) \ { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } static struct ieee80211_channel mtk_5ghz_channels[] = { CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0), CHAN5G(108, 0), CHAN5G(112, 0), CHAN5G(116, 0), CHAN5G(120, 0), CHAN5G(124, 0), CHAN5G(128, 0), CHAN5G(132, 0), CHAN5G(136, 0), CHAN5G(140, 0), CHAN5G(149, 0), CHAN5G(153, 0), CHAN5G(157, 0), CHAN5G(161, 0), CHAN5G(165, 0), CHAN5G(169, 0), CHAN5G(173, 0), CHAN5G(184, 0), CHAN5G(188, 0), CHAN5G(192, 0), CHAN5G(196, 0), CHAN5G(200, 0), CHAN5G(204, 0), CHAN5G(208, 0), CHAN5G(212, 0), CHAN5G(216, 0), }; /* for cfg80211 - rate table */ static struct ieee80211_rate mtk_rates[] = { RATETAB_ENT(10, 0x1000, 0), RATETAB_ENT(20, 0x1001, 0), RATETAB_ENT(55, 0x1002, 0), RATETAB_ENT(110, 0x1003, 0), /* 802.11b */ RATETAB_ENT(60, 0x2000, 0), RATETAB_ENT(90, 0x2001, 0), RATETAB_ENT(120, 0x2002, 0), RATETAB_ENT(180, 0x2003, 0), RATETAB_ENT(240, 0x2004, 0), RATETAB_ENT(360, 0x2005, 0), RATETAB_ENT(480, 0x2006, 0), RATETAB_ENT(540, 0x2007, 0), /* 802.11a/g */ }; #define mtk_a_rates (mtk_rates + 4) #define mtk_a_rates_size (sizeof(mtk_rates) / sizeof(mtk_rates[0]) - 4) #define mtk_g_rates (mtk_rates + 0) #define mtk_g_rates_size (sizeof(mtk_rates) / sizeof(mtk_rates[0]) - 0) #define MT6620_MCS_INFO \ { \ .rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0},\ .rx_highest = 0, \ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \ } #define MT6620_HT_CAP \ { \ .ht_supported = true, \ .cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 \ | IEEE80211_HT_CAP_SM_PS \ | IEEE80211_HT_CAP_GRN_FLD \ | IEEE80211_HT_CAP_SGI_20 \ | IEEE80211_HT_CAP_SGI_40, \ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, \ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, \ .mcs = MT6620_MCS_INFO, \ } /* public for both Legacy Wi-Fi / P2P access */ struct ieee80211_supported_band mtk_band_2ghz = { .band = IEEE80211_BAND_2GHZ, .channels = mtk_2ghz_channels, .n_channels = ARRAY_SIZE(mtk_2ghz_channels), .bitrates = mtk_g_rates, .n_bitrates = mtk_g_rates_size, .ht_cap = MT6620_HT_CAP, }; /* public for both Legacy Wi-Fi / P2P access */ struct ieee80211_supported_band mtk_band_5ghz = { .band = IEEE80211_BAND_5GHZ, .channels = mtk_5ghz_channels, .n_channels = ARRAY_SIZE(mtk_5ghz_channels), .bitrates = mtk_a_rates, .n_bitrates = mtk_a_rates_size, .ht_cap = MT6620_HT_CAP, }; static const UINT_32 mtk_cipher_suites[] = { /* keep WEP first, it may be removed below */ WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, /* keep last -- depends on hw flags! */ WLAN_CIPHER_SUITE_AES_CMAC }; static struct cfg80211_ops mtk_wlan_ops = { .suspend = mtk_cfg80211_suspend, .resume = mtk_cfg80211_resume, .change_virtual_intf = mtk_cfg80211_change_iface, .add_key = mtk_cfg80211_add_key, .get_key = mtk_cfg80211_get_key, .del_key = mtk_cfg80211_del_key, .set_default_key = mtk_cfg80211_set_default_key, .set_default_mgmt_key = mtk_cfg80211_set_default_mgmt_key, .get_station = mtk_cfg80211_get_station, .change_station = mtk_cfg80211_change_station, .add_station = mtk_cfg80211_add_station, .del_station = mtk_cfg80211_del_station, .scan = mtk_cfg80211_scan, .connect = mtk_cfg80211_connect, .disconnect = mtk_cfg80211_disconnect, .join_ibss = mtk_cfg80211_join_ibss, .leave_ibss = mtk_cfg80211_leave_ibss, .set_power_mgmt = mtk_cfg80211_set_power_mgmt, .set_pmksa = mtk_cfg80211_set_pmksa, .del_pmksa = mtk_cfg80211_del_pmksa, .flush_pmksa = mtk_cfg80211_flush_pmksa, .assoc = mtk_cfg80211_assoc, /* Action Frame TX/RX */ .remain_on_channel = mtk_cfg80211_remain_on_channel, .cancel_remain_on_channel = mtk_cfg80211_cancel_remain_on_channel, .mgmt_tx = mtk_cfg80211_mgmt_tx, /* .mgmt_tx_cancel_wait = mtk_cfg80211_mgmt_tx_cancel_wait, */ .mgmt_frame_register = mtk_cfg80211_mgmt_frame_register, #ifdef CONFIG_NL80211_TESTMODE .testmode_cmd = mtk_cfg80211_testmode_cmd, #endif #if (CFG_SUPPORT_TDLS == 1) .tdls_mgmt = TdlsexCfg80211TdlsMgmt, .tdls_oper = TdlsexCfg80211TdlsOper, #endif /* CFG_SUPPORT_TDLS */ #if 1 /* Remove schedule_scan because we need more verification for NLO */ .sched_scan_start = mtk_cfg80211_sched_scan_start, .sched_scan_stop = mtk_cfg80211_sched_scan_stop, #endif }; static const struct wiphy_vendor_command mtk_wlan_vendor_ops[] = { #if 0 /* Disable GScan */ { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_GET_CAPABILITIES}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_get_gscan_capabilities}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_SET_CONFIG}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_set_config}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_SET_SCAN_CONFIG}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV, .doit = mtk_cfg80211_vendor_set_scan_config}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_ENABLE_GSCAN}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_enable_scan}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_ENABLE_FULL_SCAN_RESULTS}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_enable_full_scan_results}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_GET_SCAN_RESULTS}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_get_scan_results}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_GET_CHANNEL_LIST}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_get_channel_list}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_SET_SIGNIFICANT_CHANGE_CONFIG}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_set_significant_change}, { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_SUBCMD_SET_HOTLIST}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_set_hotlist}, #endif /*Link Layer Statistics */ { { .vendor_id = GOOGLE_OUI, .subcmd = LSTATS_SUBCMD_GET_INFO}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_llstats_get_info}, { { .vendor_id = GOOGLE_OUI, .subcmd = RTT_SUBCMD_GETCAPABILITY}, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, .doit = mtk_cfg80211_vendor_get_rtt_capabilities}, }; static const struct nl80211_vendor_cmd_info mtk_wlan_vendor_events[] = { { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_EVENT_SIGNIFICANT_CHANGE_RESULTS}, { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_EVENT_HOTLIST_RESULTS_FOUND}, { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_EVENT_SCAN_RESULTS_AVAILABLE}, { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_EVENT_FULL_SCAN_RESULTS}, { .vendor_id = GOOGLE_OUI, .subcmd = RTT_EVENT_COMPLETE}, { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_EVENT_COMPLETE_SCAN}, { .vendor_id = GOOGLE_OUI, .subcmd = GSCAN_EVENT_HOTLIST_RESULTS_LOST}, }; /* There isn't a lot of sense in it, but you can transmit anything you like */ static const struct ieee80211_txrx_stypes mtk_cfg80211_ais_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_ADHOC] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) }, [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_AP] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4) }, [NL80211_IFTYPE_AP_VLAN] = { /* copy AP */ .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4) }, [NL80211_IFTYPE_P2P_CLIENT] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_GO] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4) } }; /******************************************************************************* * M A C R O S ******************************************************************************** */ /******************************************************************************* * F U N C T I O N D E C L A R A T I O N S ******************************************************************************** */ /******************************************************************************* * F U N C T I O N S ******************************************************************************** */ /*----------------------------------------------------------------------------*/ /*! * \brief Override the implementation of select queue * * \param[in] dev Pointer to struct net_device * \param[in] skb Pointer to struct skb_buff * * \return (none) */ /*----------------------------------------------------------------------------*/ unsigned int _cfg80211_classify8021d(struct sk_buff *skb) { unsigned int dscp = 0; /* skb->priority values from 256->263 are magic values * directly indicate a specific 802.1d priority. This is * to allow 802.1d priority to be passed directly in from * tags */ if (skb->priority >= 256 && skb->priority <= 263) return skb->priority - 256; switch (skb->protocol) { case htons(ETH_P_IP): dscp = ip_hdr(skb)->tos & 0xfc; break; } return dscp >> 5; } static const UINT_16 au16Wlan1dToQueueIdx[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; static UINT_16 wlanSelectQueue(struct net_device *dev, struct sk_buff *skb, void *accel_priv, select_queue_fallback_t fallback) { skb->priority = _cfg80211_classify8021d(skb); return au16Wlan1dToQueueIdx[skb->priority]; } /*----------------------------------------------------------------------------*/ /*! * \brief Load NVRAM data and translate it into REG_INFO_T * * \param[in] prGlueInfo Pointer to struct GLUE_INFO_T * \param[out] prRegInfo Pointer to struct REG_INFO_T * * \return (none) */ /*----------------------------------------------------------------------------*/ static void glLoadNvram(IN P_GLUE_INFO_T prGlueInfo, OUT P_REG_INFO_T prRegInfo) { UINT_32 i, j; UINT_8 aucTmp[2]; PUINT_8 pucDest; ASSERT(prGlueInfo); ASSERT(prRegInfo); if ((!prGlueInfo) || (!prRegInfo)) return; if (kalCfgDataRead16(prGlueInfo, 0, (PUINT_16) aucTmp) == TRUE) { prGlueInfo->fgNvramAvailable = TRUE; /* load MAC Address */ #if !defined(CONFIG_MTK_TC1_FEATURE) for (i = 0; i < PARAM_MAC_ADDR_LEN; i += sizeof(UINT_16)) { kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, aucMacAddress) + i, (PUINT_16) (((PUINT_8) prRegInfo->aucMacAddr) + i)); } #else TC1_FAC_NAME(FacReadWifiMacAddr) ((unsigned char *)prRegInfo->aucMacAddr); #endif /* load country code */ kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, aucCountryCode[0]), (PUINT_16) aucTmp); /* cast to wide characters */ prRegInfo->au2CountryCode[0] = (UINT_16) aucTmp[0]; prRegInfo->au2CountryCode[1] = (UINT_16) aucTmp[1]; /* load default normal TX power */ for (i = 0; i < sizeof(TX_PWR_PARAM_T); i += sizeof(UINT_16)) { kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, rTxPwr) + i, (PUINT_16) (((PUINT_8) &(prRegInfo->rTxPwr)) + i)); } /* load feature flags */ kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, ucTxPwrValid), (PUINT_16) aucTmp); prRegInfo->ucTxPwrValid = aucTmp[0]; prRegInfo->ucSupport5GBand = aucTmp[1]; kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, uc2G4BwFixed20M), (PUINT_16) aucTmp); prRegInfo->uc2G4BwFixed20M = aucTmp[0]; prRegInfo->uc5GBwFixed20M = aucTmp[1]; kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, ucEnable5GBand), (PUINT_16) aucTmp); prRegInfo->ucEnable5GBand = aucTmp[0]; /* load EFUSE overriding part */ for (i = 0; i < sizeof(prRegInfo->aucEFUSE); i += sizeof(UINT_16)) { kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, aucEFUSE) + i, (PUINT_16) (((PUINT_8) &(prRegInfo->aucEFUSE)) + i)); } /* load band edge tx power control */ kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, fg2G4BandEdgePwrUsed), (PUINT_16) aucTmp); prRegInfo->fg2G4BandEdgePwrUsed = (BOOLEAN) aucTmp[0]; if (aucTmp[0]) { prRegInfo->cBandEdgeMaxPwrCCK = (INT_8) aucTmp[1]; kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, cBandEdgeMaxPwrOFDM20), (PUINT_16) aucTmp); prRegInfo->cBandEdgeMaxPwrOFDM20 = (INT_8) aucTmp[0]; prRegInfo->cBandEdgeMaxPwrOFDM40 = (INT_8) aucTmp[1]; } /* load regulation subbands */ kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, ucRegChannelListMap), (PUINT_16) aucTmp); prRegInfo->eRegChannelListMap = (ENUM_REG_CH_MAP_T) aucTmp[0]; prRegInfo->ucRegChannelListIndex = aucTmp[1]; if (prRegInfo->eRegChannelListMap == REG_CH_MAP_CUSTOMIZED) { for (i = 0; i < MAX_SUBBAND_NUM; i++) { pucDest = (PUINT_8) &prRegInfo->rDomainInfo.rSubBand[i]; for (j = 0; j < 6; j += sizeof(UINT_16)) { kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, aucRegSubbandInfo) + (i * 6 + j), (PUINT_16) aucTmp); *pucDest++ = aucTmp[0]; *pucDest++ = aucTmp[1]; } } } /* load RSSI compensation */ kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, uc2GRssiCompensation), (PUINT_16) aucTmp); prRegInfo->uc2GRssiCompensation = aucTmp[0]; prRegInfo->uc5GRssiCompensation = aucTmp[1]; kalCfgDataRead16(prGlueInfo, OFFSET_OF(WIFI_CFG_PARAM_STRUCT, fgRssiCompensationValidbit), (PUINT_16) aucTmp); prRegInfo->fgRssiCompensationValidbit = aucTmp[0]; prRegInfo->ucRxAntennanumber = aucTmp[1]; } else { prGlueInfo->fgNvramAvailable = FALSE; } } #if CFG_ENABLE_WIFI_DIRECT /*----------------------------------------------------------------------------*/ /*! * \brief called by txthread, run sub module init function * * \param[in] prGlueInfo Pointer to struct GLUE_INFO_T * * \return (none) */ /*----------------------------------------------------------------------------*/ VOID wlanSubModRunInit(P_GLUE_INFO_T prGlueInfo) { /*now, we only have p2p module */ if (rSubModHandler[P2P_MODULE].fgIsInited == FALSE) { rSubModHandler[P2P_MODULE].subModInit(prGlueInfo); rSubModHandler[P2P_MODULE].fgIsInited = TRUE; } } /*----------------------------------------------------------------------------*/ /*! * \brief called by txthread, run sub module exit function * * \param[in] prGlueInfo Pointer to struct GLUE_INFO_T * * \return (none) */ /*----------------------------------------------------------------------------*/ VOID wlanSubModRunExit(P_GLUE_INFO_T prGlueInfo) { /*now, we only have p2p module */ if (rSubModHandler[P2P_MODULE].fgIsInited == TRUE) { rSubModHandler[P2P_MODULE].subModExit(prGlueInfo); rSubModHandler[P2P_MODULE].fgIsInited = FALSE; } } /*----------------------------------------------------------------------------*/ /*! * \brief set sub module init flag, force TxThread to run sub modle init * * \param[in] prGlueInfo Pointer to struct GLUE_INFO_T * * \return (none) */ /*----------------------------------------------------------------------------*/ BOOLEAN wlanSubModInit(P_GLUE_INFO_T prGlueInfo) { /* 4 Mark HALT, notify main thread to finish current job */ prGlueInfo->ulFlag |= GLUE_FLAG_SUB_MOD_INIT; /* wake up main thread */ wake_up_interruptible(&prGlueInfo->waitq); /* wait main thread finish sub module INIT */ wait_for_completion_interruptible(&prGlueInfo->rSubModComp); #if 0 if (prGlueInfo->prAdapter->fgIsP2PRegistered) p2pNetRegister(prGlueInfo); #endif return TRUE; } /*----------------------------------------------------------------------------*/ /*! * \brief set sub module exit flag, force TxThread to run sub modle exit * * \param[in] prGlueInfo Pointer to struct GLUE_INFO_T * * \return (none) */ /*----------------------------------------------------------------------------*/ BOOLEAN wlanSubModExit(P_GLUE_INFO_T prGlueInfo) { #if 0 if (prGlueInfo->prAdapter->fgIsP2PRegistered) p2pNetUnregister(prGlueInfo); #endif /* 4 Mark HALT, notify main thread to finish current job */ prGlueInfo->ulFlag |= GLUE_FLAG_SUB_MOD_EXIT; /* wake up main thread */ wake_up_interruptible(&prGlueInfo->waitq); /* wait main thread finish sub module EXIT */ wait_for_completion_interruptible(&prGlueInfo->rSubModComp); return TRUE; } /*----------------------------------------------------------------------------*/ /*! * \brief set by sub module, indicate sub module is already inserted * * \param[in] rSubModInit, function pointer point to sub module init function * \param[in] rSubModExit, function pointer point to sub module exit function * \param[in] eSubModIdx, sub module index * * \return (none) */ /*----------------------------------------------------------------------------*/ VOID wlanSubModRegisterInitExit(SUB_MODULE_INIT rSubModInit, SUB_MODULE_EXIT rSubModExit, ENUM_SUB_MODULE_IDX_T eSubModIdx) { rSubModHandler[eSubModIdx].subModInit = rSubModInit; rSubModHandler[eSubModIdx].subModExit = rSubModExit; rSubModHandler[eSubModIdx].fgIsInited = FALSE; } #if 0 /*----------------------------------------------------------------------------*/ /*! * \brief check wlan is launched or not * * \param[in] (none) * * \return TRUE, wlan is already started * FALSE, wlan is not started yet */ /*----------------------------------------------------------------------------*/ BOOLEAN wlanIsLaunched(VOID) { struct net_device *prDev = NULL; P_GLUE_INFO_T prGlueInfo = NULL; /* 4 <0> Sanity check */ ASSERT(u4WlanDevNum <= CFG_MAX_WLAN_DEVICES); if (0 == u4WlanDevNum) return FALSE; prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev; ASSERT(prDev); if (NULL == prDev) return FALSE; prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); ASSERT(prGlueInfo); if (NULL == prGlueInfo) return FALSE; return prGlueInfo->prAdapter->fgIsWlanLaunched; } #endif /*----------------------------------------------------------------------------*/ /*! * \brief Export wlan GLUE_INFO_T pointer to p2p module * * \param[in] prGlueInfo Pointer to struct GLUE_INFO_T * * \return TRUE: get GlueInfo pointer successfully * FALSE: wlan is not started yet */ /*---------------------------------------------------------------------------*/ BOOLEAN wlanExportGlueInfo(P_GLUE_INFO_T *prGlueInfoExpAddr) { struct net_device *prDev = NULL; P_GLUE_INFO_T prGlueInfo = NULL; if (0 == u4WlanDevNum) return FALSE; prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev; if (NULL == prDev) return FALSE; prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); if (NULL == prGlueInfo) return FALSE; if (FALSE == prGlueInfo->prAdapter->fgIsWlanLaunched) return FALSE; *prGlueInfoExpAddr = prGlueInfo; return TRUE; } #endif /*----------------------------------------------------------------------------*/ /*! * \brief Release prDev from wlandev_array and free tasklet object related to it. * * \param[in] prDev Pointer to struct net_device * * \return (none) */ /*----------------------------------------------------------------------------*/ static void wlanClearDevIdx(struct net_device *prDev) { int i; ASSERT(prDev); for (i = 0; i < CFG_MAX_WLAN_DEVICES; i++) { if (arWlanDevInfo[i].prDev == prDev) { arWlanDevInfo[i].prDev = NULL; u4WlanDevNum--; } } } /* end of wlanClearDevIdx() */ /*----------------------------------------------------------------------------*/ /*! * \brief Allocate an unique interface index, net_device::ifindex member for this * wlan device. Store the net_device in wlandev_array, and initialize * tasklet object related to it. * * \param[in] prDev Pointer to struct net_device * * \retval >= 0 The device number. * \retval -1 Fail to get index. */ /*----------------------------------------------------------------------------*/ static int wlanGetDevIdx(struct net_device *prDev) { int i; ASSERT(prDev); for (i = 0; i < CFG_MAX_WLAN_DEVICES; i++) { if (arWlanDevInfo[i].prDev == (struct net_device *)NULL) { /* Reserve 2 bytes space to store one digit of * device number and NULL terminator. */ arWlanDevInfo[i].prDev = prDev; u4WlanDevNum++; return i; } } return -1; } /* end of wlanGetDevIdx() */ /*----------------------------------------------------------------------------*/ /*! * \brief A method of struct net_device, a primary SOCKET interface to configure * the interface lively. Handle an ioctl call on one of our devices. * Everything Linux ioctl specific is done here. Then we pass the contents * of the ifr->data to the request message handler. * * \param[in] prDev Linux kernel netdevice * * \param[in] prIFReq Our private ioctl request structure, typed for the generic * struct ifreq so we can use ptr to function * * \param[in] cmd Command ID * * \retval WLAN_STATUS_SUCCESS The IOCTL command is executed successfully. * \retval OTHER The execution of IOCTL command is failed. */ /*----------------------------------------------------------------------------*/ int wlanDoIOCTL(struct net_device *prDev, struct ifreq *prIFReq, int i4Cmd) { P_GLUE_INFO_T prGlueInfo = NULL; int ret = 0; /* Verify input parameters for the following functions */ ASSERT(prDev && prIFReq); if (!prDev || !prIFReq) { DBGLOG(INIT, WARN, "%s Invalid input data\n", __func__); return -EINVAL; } prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); ASSERT(prGlueInfo); if (!prGlueInfo) { DBGLOG(INIT, WARN, "%s No glue info\n", __func__); return -EFAULT; } if (prGlueInfo->u4ReadyFlag == 0) return -EINVAL; /* printk ("ioctl %x\n", i4Cmd); */ if (i4Cmd == SIOCGIWPRIV) { /* 0x8B0D, get private ioctl table */ ret = wext_get_priv(prDev, prIFReq); } else if ((i4Cmd >= SIOCIWFIRST) && (i4Cmd < SIOCIWFIRSTPRIV)) { /* 0x8B00 ~ 0x8BDF, wireless extension region */ ret = wext_support_ioctl(prDev, prIFReq, i4Cmd); } else if ((i4Cmd >= SIOCIWFIRSTPRIV) && (i4Cmd < SIOCIWLASTPRIV)) { /* 0x8BE0 ~ 0x8BFF, private ioctl region */ ret = priv_support_ioctl(prDev, prIFReq, i4Cmd); } else if (i4Cmd == SIOCDEVPRIVATE + 1) { ret = priv_support_driver_cmd(prDev, prIFReq, i4Cmd); } else { DBGLOG(INIT, WARN, "Unexpected ioctl command: 0x%04x\n", i4Cmd); /* return 0 for safe? */ } return ret; } /* end of wlanDoIOCTL() */ /*----------------------------------------------------------------------------*/ /*! * \brief This function is to set multicast list and set rx mode. * * \param[in] prDev Pointer to struct net_device * * \return (none) */ /*----------------------------------------------------------------------------*/ static struct delayed_work workq; static struct net_device *gPrDev; static BOOLEAN fgIsWorkMcStart = FALSE; static BOOLEAN fgIsWorkMcEverInit = FALSE; static struct wireless_dev *gprWdev; #ifdef CONFIG_PM static const struct wiphy_wowlan_support wlan_wowlan_support = { .flags = WIPHY_WOWLAN_DISCONNECT | WIPHY_WOWLAN_ANY, }; #endif static void createWirelessDevice(void) { struct wiphy *prWiphy = NULL; struct wireless_dev *prWdev = NULL; #if CFG_SUPPORT_PERSIST_NETDEV struct net_device *prNetDev = NULL; #endif /* <1.1> Create wireless_dev */ prWdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); if (!prWdev) { DBGLOG(INIT, ERROR, "Allocating memory to wireless_dev context failed\n"); return; } /* <1.2> Create wiphy */ prWiphy = wiphy_new(&mtk_wlan_ops, sizeof(GLUE_INFO_T)); if (!prWiphy) { DBGLOG(INIT, ERROR, "Allocating memory to wiphy device failed\n"); goto free_wdev; } /* <1.3> configure wireless_dev & wiphy */ prWdev->iftype = NL80211_IFTYPE_STATION; prWiphy->max_scan_ssids = 1; /* FIXME: for combo scan */ prWiphy->max_scan_ie_len = 512; prWiphy->max_sched_scan_ssids = CFG_SCAN_SSID_MAX_NUM; prWiphy->max_match_sets = CFG_SCAN_SSID_MATCH_MAX_NUM; prWiphy->max_sched_scan_ie_len = CFG_CFG80211_IE_BUF_LEN; prWiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); prWiphy->bands[IEEE80211_BAND_2GHZ] = &mtk_band_2ghz; /* always assign 5Ghz bands here, if the chip is not support 5Ghz, bands[IEEE80211_BAND_5GHZ] will be assign to NULL */ prWiphy->bands[IEEE80211_BAND_5GHZ] = &mtk_band_5ghz; prWiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; prWiphy->cipher_suites = (const u32 *)mtk_cipher_suites; prWiphy->n_cipher_suites = ARRAY_SIZE(mtk_cipher_suites); prWiphy->flags = WIPHY_FLAG_SUPPORTS_FW_ROAM | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_SUPPORTS_SCHED_SCAN; prWiphy->regulatory_flags = REGULATORY_CUSTOM_REG; #if (CFG_SUPPORT_TDLS == 1) TDLSEX_WIPHY_FLAGS_INIT(prWiphy->flags); #endif /* CFG_SUPPORT_TDLS */ prWiphy->max_remain_on_channel_duration = 5000; prWiphy->mgmt_stypes = mtk_cfg80211_ais_default_mgmt_stypes; prWiphy->vendor_commands = mtk_wlan_vendor_ops; prWiphy->n_vendor_commands = sizeof(mtk_wlan_vendor_ops) / sizeof(struct wiphy_vendor_command); prWiphy->vendor_events = mtk_wlan_vendor_events; prWiphy->n_vendor_events = ARRAY_SIZE(mtk_wlan_vendor_events); /* <1.4> wowlan support */ #ifdef CONFIG_PM prWiphy->wowlan = &wlan_wowlan_support; #endif #ifdef CONFIG_CFG80211_WEXT /* <1.5> Use wireless extension to replace IOCTL */ prWiphy->wext = &wext_handler_def; #endif if (wiphy_register(prWiphy) < 0) { DBGLOG(INIT, ERROR, "wiphy_register error\n"); goto free_wiphy; } prWdev->wiphy = prWiphy; #if CFG_SUPPORT_PERSIST_NETDEV /* <2> allocate and register net_device */ #if CFG_TC1_FEATURE if (wlan_if_changed) prNetDev = alloc_netdev_mq(sizeof(P_GLUE_INFO_T), NIC_INF_NAME_IN_AP_MODE, NET_NAME_PREDICTABLE, ether_setup, CFG_MAX_TXQ_NUM); else #else prNetDev = alloc_netdev_mq(sizeof(P_GLUE_INFO_T), NIC_INF_NAME, NET_NAME_PREDICTABLE, ether_setup, CFG_MAX_TXQ_NUM); #endif if (!prNetDev) { DBGLOG(INIT, ERROR, "Allocating memory to net_device context failed\n"); goto unregister_wiphy; } *((P_GLUE_INFO_T *) netdev_priv(prNetDev)) = (P_GLUE_INFO_T) wiphy_priv(prWiphy); prNetDev->netdev_ops = &wlan_netdev_ops; #ifdef CONFIG_WIRELESS_EXT prNetDev->wireless_handlers = &wext_handler_def; #endif netif_carrier_off(prNetDev); netif_tx_stop_all_queues(prNetDev); /* <2.1> co-relate with wireless_dev bi-directionally */ prNetDev->ieee80211_ptr = prWdev; prWdev->netdev = prNetDev; #if CFG_TCP_IP_CHKSUM_OFFLOAD prNetDev->features = NETIF_F_HW_CSUM; #endif /* <2.2> co-relate net device & device tree */ SET_NETDEV_DEV(prNetDev, wiphy_dev(prWiphy)); /* <2.3> register net_device */ if (register_netdev(prWdev->netdev) < 0) { DBGLOG(INIT, ERROR, "wlanNetRegister: net_device context is not registered.\n"); goto unregister_wiphy; } #endif /* CFG_SUPPORT_PERSIST_NETDEV */ gprWdev = prWdev; DBGLOG(INIT, INFO, "create wireless device success\n"); return; #if CFG_SUPPORT_PERSIST_NETDEV unregister_wiphy: wiphy_unregister(prWiphy); #endif free_wiphy: wiphy_free(prWiphy); free_wdev: kfree(prWdev); } static void destroyWirelessDevice(void) { #if CFG_SUPPORT_PERSIST_NETDEV unregister_netdev(gprWdev->netdev); free_netdev(gprWdev->netdev); #endif wiphy_unregister(gprWdev->wiphy); wiphy_free(gprWdev->wiphy); kfree(gprWdev); gprWdev = NULL; } static void wlanSetMulticastList(struct net_device *prDev) { gPrDev = prDev; schedule_delayed_work(&workq, 0); } /* FIXME: Since we cannot sleep in the wlanSetMulticastList, we arrange * another workqueue for sleeping. We don't want to block * tx_thread, so we can't let tx_thread to do this */ static void wlanSetMulticastListWorkQueue(struct work_struct *work) { P_GLUE_INFO_T prGlueInfo = NULL; UINT_32 u4PacketFilter = 0; UINT_32 u4SetInfoLen; struct net_device *prDev = gPrDev; fgIsWorkMcStart = TRUE; DBGLOG(INIT, INFO, "wlanSetMulticastListWorkQueue start...\n"); if (kalHaltLock(KAL_HALT_LOCK_TIMEOUT_NORMAL_CASE)) return; if (kalIsHalted()) { fgIsWorkMcStart = FALSE; kalHaltUnlock(); return; } prGlueInfo = (NULL != prDev) ? *((P_GLUE_INFO_T *) netdev_priv(prDev)) : NULL; ASSERT(prDev); ASSERT(prGlueInfo); if (!prDev || !prGlueInfo) { DBGLOG(INIT, WARN, "abnormal dev or skb: prDev(0x%p), prGlueInfo(0x%p)\n", prDev, prGlueInfo); fgIsWorkMcStart = FALSE; kalHaltUnlock(); return; } if (prDev->flags & IFF_PROMISC) u4PacketFilter |= PARAM_PACKET_FILTER_PROMISCUOUS; if (prDev->flags & IFF_BROADCAST) u4PacketFilter |= PARAM_PACKET_FILTER_BROADCAST; if (prDev->flags & IFF_MULTICAST) { if ((prDev->flags & IFF_ALLMULTI) || (netdev_mc_count(prDev) > MAX_NUM_GROUP_ADDR)) { u4PacketFilter |= PARAM_PACKET_FILTER_ALL_MULTICAST; } else { u4PacketFilter |= PARAM_PACKET_FILTER_MULTICAST; } } kalHaltUnlock(); if (kalIoctl(prGlueInfo, wlanoidSetCurrentPacketFilter, &u4PacketFilter, sizeof(u4PacketFilter), FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen) != WLAN_STATUS_SUCCESS) { fgIsWorkMcStart = FALSE; return; } if (u4PacketFilter & PARAM_PACKET_FILTER_MULTICAST) { /* Prepare multicast address list */ struct netdev_hw_addr *ha; PUINT_8 prMCAddrList = NULL; UINT_32 i = 0; if (kalHaltLock(KAL_HALT_LOCK_TIMEOUT_NORMAL_CASE)) return; if (kalIsHalted()) { fgIsWorkMcStart = FALSE; kalHaltUnlock(); /*DBGLOG(INIT, WARN, "wlanSetMulticastListWorkQueue g_u4HaltFlag=%d\n", g_u4HaltFlag);*/ return; } prMCAddrList = kalMemAlloc(MAX_NUM_GROUP_ADDR * ETH_ALEN, VIR_MEM_TYPE); netdev_for_each_mc_addr(ha, prDev) { if (i < MAX_NUM_GROUP_ADDR) { memcpy((prMCAddrList + i * ETH_ALEN), ha->addr, ETH_ALEN); i++; } } kalHaltUnlock(); kalIoctl(prGlueInfo, wlanoidSetMulticastList, prMCAddrList, (i * ETH_ALEN), FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen); kalMemFree(prMCAddrList, VIR_MEM_TYPE, MAX_NUM_GROUP_ADDR * ETH_ALEN); } fgIsWorkMcStart = FALSE; DBGLOG(INIT, INFO, "wlanSetMulticastListWorkQueue end\n"); } /* end of wlanSetMulticastList() */ /*----------------------------------------------------------------------------*/ /*! * \brief To indicate scheduled scan has been stopped * * \param[in] * prGlueInfo * * \return * None */ /*----------------------------------------------------------------------------*/ VOID wlanSchedScanStoppedWorkQueue(struct work_struct *work) { P_GLUE_INFO_T prGlueInfo = NULL; struct net_device *prDev = gPrDev; prGlueInfo = (NULL != prDev) ? *((P_GLUE_INFO_T *) netdev_priv(prDev)) : NULL; if (!prGlueInfo) { DBGLOG(SCN, ERROR, "prGlueInfo == NULL unexpected\n"); return; } /* 2. indication to cfg80211 */ /* 20150205 change cfg80211_sched_scan_stopped to work queue due to sched_scan_mtx dead lock issue */ cfg80211_sched_scan_stopped(priv_to_wiphy(prGlueInfo)); DBGLOG(SCN, INFO, "cfg80211_sched_scan_stopped event send done\n"); } /* FIXME: Since we cannot sleep in the wlanSetMulticastList, we arrange * another workqueue for sleeping. We don't want to block * tx_thread, so we can't let tx_thread to do this */ void p2pSetMulticastListWorkQueueWrapper(P_GLUE_INFO_T prGlueInfo) { ASSERT(prGlueInfo); if (!prGlueInfo) { DBGLOG(INIT, WARN, "abnormal dev or skb: prGlueInfo(0x%p)\n", prGlueInfo); return; } #if CFG_ENABLE_WIFI_DIRECT if (prGlueInfo->prAdapter->fgIsP2PRegistered) mtk_p2p_wext_set_Multicastlist(prGlueInfo); #endif } /* end of p2pSetMulticastListWorkQueueWrapper() */ /*----------------------------------------------------------------------------*/ /*! * \brief This function is TX entry point of NET DEVICE. * * \param[in] prSkb Pointer of the sk_buff to be sent * \param[in] prDev Pointer to struct net_device * * \retval NETDEV_TX_OK - on success. * \retval NETDEV_TX_BUSY - on failure, packet will be discarded by upper layer. */ /*----------------------------------------------------------------------------*/ int wlanHardStartXmit(struct sk_buff *prSkb, struct net_device *prDev) { P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); P_QUE_ENTRY_T prQueueEntry = NULL; P_QUE_T prTxQueue = NULL; UINT_16 u2QueueIdx = 0; #if (CFG_SUPPORT_TDLS_DBG == 1) UINT16 u2Identifier = 0; #endif #if CFG_BOW_TEST UINT_32 i; #endif GLUE_SPIN_LOCK_DECLARATION(); ASSERT(prSkb); ASSERT(prDev); ASSERT(prGlueInfo); #if (CFG_SUPPORT_TDLS_DBG == 1) { UINT8 *pkt = prSkb->data; if ((*(pkt + 12) == 0x08) && (*(pkt + 13) == 0x00)) { /* ip */ u2Identifier = ((*(pkt + 18)) << 8) | (*(pkt + 19)); /* u2TdlsTxSeq[u4TdlsTxSeqId ++] = u2Identifier; */ DBGLOG(INIT, INFO, " %d\n", u2Identifier); } } #endif /* check if WiFi is halt */ if (prGlueInfo->ulFlag & GLUE_FLAG_HALT) { DBGLOG(INIT, INFO, "GLUE_FLAG_HALT skip tx\n"); dev_kfree_skb(prSkb); return NETDEV_TX_OK; } #if CFG_SUPPORT_HOTSPOT_2_0 if (prGlueInfo->fgIsDad) { /* kalPrint("[Passpoint R2] Due to ipv4_dad...TX is forbidden\n"); */ dev_kfree_skb(prSkb); return NETDEV_TX_OK; } if (prGlueInfo->fgIs6Dad) { /* kalPrint("[Passpoint R2] Due to ipv6_dad...TX is forbidden\n"); */ dev_kfree_skb(prSkb); return NETDEV_TX_OK; } #endif STATS_TX_TIME_ARRIVE(prSkb); prQueueEntry = (P_QUE_ENTRY_T) GLUE_GET_PKT_QUEUE_ENTRY(prSkb); prTxQueue = &prGlueInfo->rTxQueue; #if CFG_BOW_TEST DBGLOG(BOW, TRACE, "sk_buff->len: %d\n", prSkb->len); DBGLOG(BOW, TRACE, "sk_buff->data_len: %d\n", prSkb->data_len); DBGLOG(BOW, TRACE, "sk_buff->data:\n"); for (i = 0; i < prSkb->len; i++) { DBGLOG(BOW, TRACE, "%4x", prSkb->data[i]); if ((i + 1) % 16 == 0) DBGLOG(BOW, TRACE, "\n"); } DBGLOG(BOW, TRACE, "\n"); #endif if (wlanProcessSecurityFrame(prGlueInfo->prAdapter, (P_NATIVE_PACKET) prSkb) == FALSE) { /* non-1x packets */ #if CFG_DBG_GPIO_PINS { /* TX request from OS */ mtk_wcn_stp_debug_gpio_assert(IDX_TX_REQ, DBG_TIE_LOW); kalUdelay(1); mtk_wcn_stp_debug_gpio_assert(IDX_TX_REQ, DBG_TIE_HIGH); } #endif u2QueueIdx = skb_get_queue_mapping(prSkb); ASSERT(u2QueueIdx < CFG_MAX_TXQ_NUM); #if CFG_ENABLE_PKT_LIFETIME_PROFILE GLUE_SET_PKT_ARRIVAL_TIME(prSkb, kalGetTimeTick()); #endif GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingFrameNum); if (u2QueueIdx < CFG_MAX_TXQ_NUM) GLUE_INC_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[NETWORK_TYPE_AIS_INDEX][u2QueueIdx]); GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE); QUEUE_INSERT_TAIL(prTxQueue, prQueueEntry); GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_TX_QUE); /* GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingFrameNum); */ /* GLUE_INC_REF_CNT(prGlueInfo->ai4TxPendingFrameNumPerQueue[NETWORK_TYPE_AIS_INDEX][u2QueueIdx]); */ if (u2QueueIdx < CFG_MAX_TXQ_NUM) { if (prGlueInfo->ai4TxPendingFrameNumPerQueue[NETWORK_TYPE_AIS_INDEX][u2QueueIdx] >= CFG_TX_STOP_NETIF_PER_QUEUE_THRESHOLD) { netif_stop_subqueue(prDev, u2QueueIdx); #if (CONF_HIF_LOOPBACK_AUTO == 1) prGlueInfo->rHifInfo.HifLoopbkFlg |= 0x01; #endif /* CONF_HIF_LOOPBACK_AUTO */ } } } else { /* printk("is security frame\n"); */ GLUE_INC_REF_CNT(prGlueInfo->i4TxPendingSecurityFrameNum); } DBGLOG(TX, EVENT, "\n+++++ pending frame %d len = %d +++++\n", prGlueInfo->i4TxPendingFrameNum, prSkb->len); prGlueInfo->rNetDevStats.tx_bytes += prSkb->len; prGlueInfo->rNetDevStats.tx_packets++; if (netif_carrier_ok(prDev)) kalPerMonStart(prGlueInfo); /* set GLUE_FLAG_TXREQ_BIT */ /* pr->u4Flag |= GLUE_FLAG_TXREQ; */ /* wake_up_interruptible(&prGlueInfo->waitq); */ kalSetEvent(prGlueInfo); /* For Linux, we'll always return OK FLAG, because we'll free this skb by ourself */ return NETDEV_TX_OK; } /* end of wlanHardStartXmit() */ /*----------------------------------------------------------------------------*/ /*! * \brief A method of struct net_device, to get the network interface statistical * information. * * Whenever an application needs to get statistics for the interface, this method * is called. This happens, for example, when ifconfig or netstat -i is run. * * \param[in] prDev Pointer to struct net_device. * * \return net_device_stats buffer pointer. */ /*----------------------------------------------------------------------------*/ struct net_device_stats *wlanGetStats(IN struct net_device *prDev) { P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); #if 0 WLAN_STATUS rStatus; UINT_32 u4XmitError = 0; UINT_32 u4XmitOk = 0; UINT_32 u4RecvError = 0; UINT_32 u4RecvOk = 0; UINT_32 u4BufLen; ASSERT(prDev); /* @FIX ME: need a more clear way to do this */ rStatus = kalIoctl(prGlueInfo, wlanoidQueryXmitError, &u4XmitError, sizeof(UINT_32), TRUE, TRUE, TRUE, &u4BufLen); rStatus = kalIoctl(prGlueInfo, wlanoidQueryXmitOk, &u4XmitOk, sizeof(UINT_32), TRUE, TRUE, TRUE, &u4BufLen); rStatus = kalIoctl(prGlueInfo, wlanoidQueryRcvOk, &u4RecvOk, sizeof(UINT_32), TRUE, TRUE, TRUE, &u4BufLen); rStatus = kalIoctl(prGlueInfo, wlanoidQueryRcvError, &u4RecvError, sizeof(UINT_32), TRUE, TRUE, TRUE, &u4BufLen); prGlueInfo->rNetDevStats.rx_packets = u4RecvOk; prGlueInfo->rNetDevStats.tx_packets = u4XmitOk; prGlueInfo->rNetDevStats.tx_errors = u4XmitError; prGlueInfo->rNetDevStats.rx_errors = u4RecvError; /* prGlueInfo->rNetDevStats.rx_bytes = rCustomNetDevStats.u4RxBytes; */ /* prGlueInfo->rNetDevStats.tx_bytes = rCustomNetDevStats.u4TxBytes; */ /* prGlueInfo->rNetDevStats.rx_errors = rCustomNetDevStats.u4RxErrors; */ /* prGlueInfo->rNetDevStats.multicast = rCustomNetDevStats.u4Multicast; */ #endif /* prGlueInfo->rNetDevStats.rx_packets = 0; */ /* prGlueInfo->rNetDevStats.tx_packets = 0; */ prGlueInfo->rNetDevStats.tx_errors = 0; prGlueInfo->rNetDevStats.rx_errors = 0; /* prGlueInfo->rNetDevStats.rx_bytes = 0; */ /* prGlueInfo->rNetDevStats.tx_bytes = 0; */ prGlueInfo->rNetDevStats.rx_errors = 0; prGlueInfo->rNetDevStats.multicast = 0; return &prGlueInfo->rNetDevStats; } /* end of wlanGetStats() */ /*----------------------------------------------------------------------------*/ /*! * \brief A function for prDev->init * * \param[in] prDev Pointer to struct net_device. * * \retval 0 The execution of wlanInit succeeds. * \retval -ENXIO No such device. */ /*----------------------------------------------------------------------------*/ static int wlanInit(struct net_device *prDev) { P_GLUE_INFO_T prGlueInfo = NULL; if (fgIsWorkMcEverInit == FALSE) { if (!prDev) return -ENXIO; prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); INIT_DELAYED_WORK(&workq, wlanSetMulticastListWorkQueue); /* 20150205 work queue for sched_scan */ INIT_DELAYED_WORK(&sched_workq, wlanSchedScanStoppedWorkQueue); fgIsWorkMcEverInit = TRUE; } return 0; /* success */ } /* end of wlanInit() */ /*----------------------------------------------------------------------------*/ /*! * \brief A function for prDev->uninit * * \param[in] prDev Pointer to struct net_device. * * \return (none) */ /*----------------------------------------------------------------------------*/ static void wlanUninit(struct net_device *prDev) { } /* end of wlanUninit() */ /*----------------------------------------------------------------------------*/ /*! * \brief A function for prDev->open * * \param[in] prDev Pointer to struct net_device. * * \retval 0 The execution of wlanOpen succeeds. * \retval < 0 The execution of wlanOpen failed. */ /*----------------------------------------------------------------------------*/ static int wlanOpen(struct net_device *prDev) { ASSERT(prDev); netif_tx_start_all_queues(prDev); return 0; /* success */ } /* end of wlanOpen() */ /*----------------------------------------------------------------------------*/ /*! * \brief A function for prDev->stop * * \param[in] prDev Pointer to struct net_device. * * \retval 0 The execution of wlanStop succeeds. * \retval < 0 The execution of wlanStop failed. */ /*----------------------------------------------------------------------------*/ static int wlanStop(struct net_device *prDev) { P_GLUE_INFO_T prGlueInfo = NULL; struct cfg80211_scan_request *prScanRequest = NULL; GLUE_SPIN_LOCK_DECLARATION(); ASSERT(prDev); prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); /* CFG80211 down */ GLUE_ACQUIRE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); if (prGlueInfo->prScanRequest != NULL) { prScanRequest = prGlueInfo->prScanRequest; prGlueInfo->prScanRequest = NULL; } GLUE_RELEASE_SPIN_LOCK(prGlueInfo, SPIN_LOCK_NET_DEV); if (prScanRequest) cfg80211_scan_done(prScanRequest, TRUE); netif_tx_stop_all_queues(prDev); return 0; /* success */ } /* end of wlanStop() */ /*----------------------------------------------------------------------------*/ /*! * \brief Update Channel table for cfg80211 for Wi-Fi Direct based on current country code * * \param[in] prGlueInfo Pointer to glue info * * \return none */ /*----------------------------------------------------------------------------*/ VOID wlanUpdateChannelTable(P_GLUE_INFO_T prGlueInfo) { UINT_8 i, j; UINT_8 ucNumOfChannel; RF_CHANNEL_INFO_T aucChannelList[ARRAY_SIZE(mtk_2ghz_channels) + ARRAY_SIZE(mtk_5ghz_channels)]; /* 1. Disable all channel */ for (i = 0; i < ARRAY_SIZE(mtk_2ghz_channels); i++) { mtk_2ghz_channels[i].flags |= IEEE80211_CHAN_DISABLED; mtk_2ghz_channels[i].orig_flags |= IEEE80211_CHAN_DISABLED; } for (i = 0; i < ARRAY_SIZE(mtk_5ghz_channels); i++) { mtk_5ghz_channels[i].flags |= IEEE80211_CHAN_DISABLED; mtk_5ghz_channels[i].orig_flags |= IEEE80211_CHAN_DISABLED; } /* 2. Get current domain channel list */ rlmDomainGetChnlList(prGlueInfo->prAdapter, BAND_NULL, ARRAY_SIZE(mtk_2ghz_channels) + ARRAY_SIZE(mtk_5ghz_channels), &ucNumOfChannel, aucChannelList); /* 3. Enable specific channel based on domain channel list */ for (i = 0; i < ucNumOfChannel; i++) { switch (aucChannelList[i].eBand) { case BAND_2G4: for (j = 0; j < ARRAY_SIZE(mtk_2ghz_channels); j++) { if (mtk_2ghz_channels[j].hw_value == aucChannelList[i].ucChannelNum) { mtk_2ghz_channels[j].flags &= ~IEEE80211_CHAN_DISABLED; mtk_2ghz_channels[j].orig_flags &= ~IEEE80211_CHAN_DISABLED; break; } } break; case BAND_5G: for (j = 0; j < ARRAY_SIZE(mtk_5ghz_channels); j++) { if (mtk_5ghz_channels[j].hw_value == aucChannelList[i].ucChannelNum) { mtk_5ghz_channels[j].flags &= ~IEEE80211_CHAN_DISABLED; mtk_5ghz_channels[j].orig_flags &= ~IEEE80211_CHAN_DISABLED; break; } } break; default: break; } } } /*----------------------------------------------------------------------------*/ /*! * \brief Register the device to the kernel and return the index. * * \param[in] prDev Pointer to struct net_device. * * \retval 0 The execution of wlanNetRegister succeeds. * \retval < 0 The execution of wlanNetRegister failed. */ /*----------------------------------------------------------------------------*/ static INT_32 wlanNetRegister(struct wireless_dev *prWdev) { P_GLUE_INFO_T prGlueInfo; INT_32 i4DevIdx = -1; ASSERT(prWdev); do { if (!prWdev) break; prGlueInfo = (P_GLUE_INFO_T) wiphy_priv(prWdev->wiphy); i4DevIdx = wlanGetDevIdx(prWdev->netdev); if (i4DevIdx < 0) { DBGLOG(INIT, ERROR, "wlanNetRegister: net_device number exceeds.\n"); break; } /* adjust channel support status */ wlanUpdateChannelTable(prGlueInfo); #if !CFG_SUPPORT_PERSIST_NETDEV if (register_netdev(prWdev->netdev) < 0) { DBGLOG(INIT, ERROR, "wlanNetRegister: net_device context is not registered.\n"); wiphy_unregister(prWdev->wiphy); wlanClearDevIdx(prWdev->netdev); i4DevIdx = -1; } #endif if (i4DevIdx != -1) prGlueInfo->fgIsRegistered = TRUE; } while (FALSE); return i4DevIdx; /* success */ } /* end of wlanNetRegister() */ /*----------------------------------------------------------------------------*/ /*! * \brief Unregister the device from the kernel * * \param[in] prWdev Pointer to struct net_device. * * \return (none) */ /*----------------------------------------------------------------------------*/ static VOID wlanNetUnregister(struct wireless_dev *prWdev) { P_GLUE_INFO_T prGlueInfo; if (!prWdev) { DBGLOG(INIT, ERROR, "wlanNetUnregister: The device context is NULL\n"); return; } DBGLOG(INIT, TRACE, "unregister net_dev(0x%p)\n", prWdev->netdev); prGlueInfo = (P_GLUE_INFO_T) wiphy_priv(prWdev->wiphy); wlanClearDevIdx(prWdev->netdev); #if !CFG_SUPPORT_PERSIST_NETDEV unregister_netdev(prWdev->netdev); #endif prGlueInfo->fgIsRegistered = FALSE; DBGLOG(INIT, INFO, "unregister wireless_dev(0x%p), ifindex=%d\n", prWdev, prWdev->netdev->ifindex); } /* end of wlanNetUnregister() */ static const struct net_device_ops wlan_netdev_ops = { .ndo_open = wlanOpen, .ndo_stop = wlanStop, .ndo_set_rx_mode = wlanSetMulticastList, .ndo_get_stats = wlanGetStats, .ndo_do_ioctl = wlanDoIOCTL, .ndo_start_xmit = wlanHardStartXmit, .ndo_init = wlanInit, .ndo_uninit = wlanUninit, .ndo_select_queue = wlanSelectQueue, }; /*----------------------------------------------------------------------------*/ /*! * \brief A method for creating Linux NET4 struct net_device object and the * private data(prGlueInfo and prAdapter). Setup the IO address to the HIF. * Assign the function pointer to the net_device object * * \param[in] pvData Memory address for the device * * \retval Not null The wireless_dev object. * \retval NULL Fail to create wireless_dev object */ /*----------------------------------------------------------------------------*/ static struct lock_class_key rSpinKey[SPIN_LOCK_NUM]; static struct wireless_dev *wlanNetCreate(PVOID pvData) { P_GLUE_INFO_T prGlueInfo = NULL; struct wireless_dev *prWdev = gprWdev; UINT_32 i; struct device *prDev; if (!prWdev) { DBGLOG(INIT, ERROR, "Allocating memory to wireless_dev context failed\n"); return NULL; } /* 4 <1> co-relate wiphy & prDev */ #if MTK_WCN_HIF_SDIO mtk_wcn_hif_sdio_get_dev(*((MTK_WCN_HIF_SDIO_CLTCTX *) pvData), &prDev); #else /* prDev = &((struct sdio_func *) pvData)->dev; //samp */ prDev = pvData; /* samp */ #endif if (!prDev) DBGLOG(INIT, WARN, "unable to get struct dev for wlan\n"); /* don't set prDev as parent of wiphy->dev, because we have done device_add in driver init. if we set parent here, parent will be not able to know this child, and may occurs a KE in device_shutdown, to free wiphy->dev, because his parent has been freed. */ /*set_wiphy_dev(prWdev->wiphy, prDev);*/ #if !CFG_SUPPORT_PERSIST_NETDEV /* 4 <3> Initial Glue structure */ prGlueInfo = (P_GLUE_INFO_T) wiphy_priv(prWdev->wiphy); kalMemZero(prGlueInfo, sizeof(GLUE_INFO_T)); /* 4 <3.1> Create net device */ #if CFG_TC1_FEATURE if (wlan_if_changed) { prGlueInfo->prDevHandler = alloc_netdev_mq(sizeof(P_GLUE_INFO_T), NIC_INF_NAME_IN_AP_MODE, NET_NAME_PREDICTABLE, ether_setup, CFG_MAX_TXQ_NUM); } else { prGlueInfo->prDevHandler = alloc_netdev_mq(sizeof(P_GLUE_INFO_T), NIC_INF_NAME, NET_NAME_PREDICTABLE, ether_setup, CFG_MAX_TXQ_NUM); } #else prGlueInfo->prDevHandler = alloc_netdev_mq(sizeof(P_GLUE_INFO_T), NIC_INF_NAME, NET_NAME_PREDICTABLE, ether_setup, CFG_MAX_TXQ_NUM); #endif if (!prGlueInfo->prDevHandler) { DBGLOG(INIT, ERROR, "Allocating memory to net_device context failed\n"); return NULL; } DBGLOG(INIT, INFO, "net_device prDev(0x%p) allocated ifindex=%d\n", prGlueInfo->prDevHandler, prGlueInfo->prDevHandler->ifindex); /* 4 <3.1.1> initialize net device varaiables */ *((P_GLUE_INFO_T *) netdev_priv(prGlueInfo->prDevHandler)) = prGlueInfo; prGlueInfo->prDevHandler->netdev_ops = &wlan_netdev_ops; #ifdef CONFIG_WIRELESS_EXT prGlueInfo->prDevHandler->wireless_handlers = &wext_handler_def; #endif netif_carrier_off(prGlueInfo->prDevHandler); netif_tx_stop_all_queues(prGlueInfo->prDevHandler); /* 4 <3.1.2> co-relate with wiphy bi-directionally */ prGlueInfo->prDevHandler->ieee80211_ptr = prWdev; #if CFG_TCP_IP_CHKSUM_OFFLOAD prGlueInfo->prDevHandler->features = NETIF_F_HW_CSUM; #endif prWdev->netdev = prGlueInfo->prDevHandler; /* 4 <3.1.3> co-relate net device & prDev */ /*SET_NETDEV_DEV(prGlueInfo->prDevHandler, wiphy_dev(prWdev->wiphy));*/ SET_NETDEV_DEV(prGlueInfo->prDevHandler, prDev); #else /* CFG_SUPPORT_PERSIST_NETDEV */ prGlueInfo->prDevHandler = gprWdev->netdev; #endif /* CFG_SUPPORT_PERSIST_NETDEV */ /* 4 <3.2> initiali glue variables */ prGlueInfo->eParamMediaStateIndicated = PARAM_MEDIA_STATE_DISCONNECTED; prGlueInfo->ePowerState = ParamDeviceStateD0; prGlueInfo->fgIsMacAddrOverride = FALSE; prGlueInfo->fgIsRegistered = FALSE; prGlueInfo->prScanRequest = NULL; #if CFG_SUPPORT_HOTSPOT_2_0 /* Init DAD */ prGlueInfo->fgIsDad = FALSE; prGlueInfo->fgIs6Dad = FALSE; kalMemZero(prGlueInfo->aucDADipv4, 4); kalMemZero(prGlueInfo->aucDADipv6, 16); #endif init_completion(&prGlueInfo->rScanComp); init_completion(&prGlueInfo->rHaltComp); init_completion(&prGlueInfo->rPendComp); #if CFG_ENABLE_WIFI_DIRECT init_completion(&prGlueInfo->rSubModComp); #endif /* initialize timer for OID timeout checker */ kalOsTimerInitialize(prGlueInfo, kalTimeoutHandler); for (i = 0; i < SPIN_LOCK_NUM; i++) { spin_lock_init(&prGlueInfo->rSpinLock[i]); lockdep_set_class(&prGlueInfo->rSpinLock[i], &rSpinKey[i]); } /* initialize semaphore for ioctl */ sema_init(&prGlueInfo->ioctl_sem, 1); glSetHifInfo(prGlueInfo, (ULONG) pvData); /* 4 <8> Init Queues */ init_waitqueue_head(&prGlueInfo->waitq); QUEUE_INITIALIZE(&prGlueInfo->rCmdQueue); QUEUE_INITIALIZE(&prGlueInfo->rTxQueue); /* 4 <4> Create Adapter structure */ prGlueInfo->prAdapter = (P_ADAPTER_T) wlanAdapterCreate(prGlueInfo); if (!prGlueInfo->prAdapter) { DBGLOG(INIT, ERROR, "Allocating memory to adapter failed\n"); return NULL; } KAL_WAKE_LOCK_INIT(prAdapter, &prGlueInfo->rAhbIsrWakeLock, "WLAN AHB ISR"); #if CFG_SUPPORT_PERSIST_NETDEV dev_open(prGlueInfo->prDevHandler); netif_carrier_off(prGlueInfo->prDevHandler); netif_tx_stop_all_queues(prGlueInfo->prDevHandler); #endif return prWdev; } /* end of wlanNetCreate() */ /*----------------------------------------------------------------------------*/ /*! * \brief Destroying the struct net_device object and the private data. * * \param[in] prWdev Pointer to struct wireless_dev. * * \return (none) */ /*----------------------------------------------------------------------------*/ static VOID wlanNetDestroy(struct wireless_dev *prWdev) { P_GLUE_INFO_T prGlueInfo = NULL; ASSERT(prWdev); if (!prWdev) { DBGLOG(INIT, ERROR, "wlanNetDestroy: The device context is NULL\n"); return; } /* prGlueInfo is allocated with net_device */ prGlueInfo = (P_GLUE_INFO_T) wiphy_priv(prWdev->wiphy); ASSERT(prGlueInfo); /* destroy kal OS timer */ kalCancelTimer(prGlueInfo); glClearHifInfo(prGlueInfo); wlanAdapterDestroy(prGlueInfo->prAdapter); prGlueInfo->prAdapter = NULL; #if CFG_SUPPORT_PERSIST_NETDEV /* take the net_device to down state */ dev_close(prGlueInfo->prDevHandler); #else /* Free net_device and private data prGlueInfo, which are allocated by alloc_netdev(). */ free_netdev(prWdev->netdev); #endif } /* end of wlanNetDestroy() */ #ifndef CONFIG_X86 UINT_8 g_aucBufIpAddr[32] = { 0 }; static void wlanNotifyFwSuspend(P_GLUE_INFO_T prGlueInfo, BOOLEAN fgSuspend) { WLAN_STATUS rStatus = WLAN_STATUS_FAILURE; UINT_32 u4SetInfoLen; rStatus = kalIoctl(prGlueInfo, wlanoidNotifyFwSuspend, (PVOID)&fgSuspend, sizeof(fgSuspend), FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen); if (rStatus != WLAN_STATUS_SUCCESS) DBGLOG(INIT, INFO, "wlanNotifyFwSuspend fail\n"); } void wlanHandleSystemSuspend(void) { WLAN_STATUS rStatus = WLAN_STATUS_FAILURE; struct net_device *prDev = NULL; P_GLUE_INFO_T prGlueInfo = NULL; UINT_8 ip[4] = { 0 }; UINT_32 u4NumIPv4 = 0; #ifdef CONFIG_IPV6 UINT_8 ip6[16] = { 0 }; /* FIX ME: avoid to allocate large memory in stack */ UINT_32 u4NumIPv6 = 0; #endif UINT_32 i; P_PARAM_NETWORK_ADDRESS_IP prParamIpAddr; #if CFG_SUPPORT_DROP_MC_PACKET UINT_32 u4PacketFilter = 0; UINT_32 u4SetInfoLen = 0; #endif /* <1> Sanity check and acquire the net_device */ ASSERT(u4WlanDevNum <= CFG_MAX_WLAN_DEVICES); if (u4WlanDevNum == 0) { DBGLOG(INIT, ERROR, "wlanEarlySuspend u4WlanDevNum==0 invalid!!\n"); return; } prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev; fgIsUnderSuspend = true; prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); ASSERT(prGlueInfo); #if CFG_SUPPORT_DROP_MC_PACKET /* new filter should not include p2p mask */ #if CFG_ENABLE_WIFI_DIRECT_CFG_80211 u4PacketFilter = prGlueInfo->prAdapter->u4OsPacketFilter & (~PARAM_PACKET_FILTER_P2P_MASK); #endif if (kalIoctl(prGlueInfo, wlanoidSetCurrentPacketFilter, &u4PacketFilter, sizeof(u4PacketFilter), FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen) != WLAN_STATUS_SUCCESS) { DBGLOG(INIT, ERROR, "set packet filter failed.\n"); } #endif if (!prDev || !(prDev->ip_ptr) || !((struct in_device *)(prDev->ip_ptr))->ifa_list || !(&(((struct in_device *)(prDev->ip_ptr))->ifa_list->ifa_local))) { goto notify_suspend; } kalMemCopy(ip, &(((struct in_device *)(prDev->ip_ptr))->ifa_list->ifa_local), sizeof(ip)); /* todo: traverse between list to find whole sets of IPv4 addresses */ if (!((ip[0] == 0) && (ip[1] == 0) && (ip[2] == 0) && (ip[3] == 0))) u4NumIPv4++; #ifdef CONFIG_IPV6 if (!prDev || !(prDev->ip6_ptr) || !((struct in_device *)(prDev->ip6_ptr))->ifa_list || !(&(((struct in_device *)(prDev->ip6_ptr))->ifa_list->ifa_local))) { goto notify_suspend; } kalMemCopy(ip6, &(((struct in_device *)(prDev->ip6_ptr))->ifa_list->ifa_local), sizeof(ip6)); DBGLOG(INIT, INFO, "ipv6 is %d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d\n", ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7], ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15] ); /* todo: traverse between list to find whole sets of IPv6 addresses */ if (!((ip6[0] == 0) && (ip6[1] == 0) && (ip6[2] == 0) && (ip6[3] == 0) && (ip6[4] == 0) && (ip6[5] == 0))) { /* Do nothing */ /* u4NumIPv6++; */ } #endif /* <7> set up the ARP filter */ { UINT_32 u4SetInfoLen = 0; UINT_32 u4Len = OFFSET_OF(PARAM_NETWORK_ADDRESS_LIST, arAddress); P_PARAM_NETWORK_ADDRESS_LIST prParamNetAddrList = (P_PARAM_NETWORK_ADDRESS_LIST) g_aucBufIpAddr; P_PARAM_NETWORK_ADDRESS prParamNetAddr = prParamNetAddrList->arAddress; kalMemZero(g_aucBufIpAddr, sizeof(g_aucBufIpAddr)); prParamNetAddrList->u4AddressCount = u4NumIPv4 + u4NumIPv6; prParamNetAddrList->u2AddressType = PARAM_PROTOCOL_ID_TCP_IP; for (i = 0; i < u4NumIPv4; i++) { prParamNetAddr->u2AddressLength = sizeof(PARAM_NETWORK_ADDRESS_IP); /* 4;; */ prParamNetAddr->u2AddressType = PARAM_PROTOCOL_ID_TCP_IP; prParamIpAddr = (P_PARAM_NETWORK_ADDRESS_IP) prParamNetAddr->aucAddress; kalMemCopy(&prParamIpAddr->in_addr, ip, sizeof(ip)); prParamNetAddr = (P_PARAM_NETWORK_ADDRESS) ((ULONG) prParamNetAddr + sizeof(PARAM_NETWORK_ADDRESS)); u4Len += OFFSET_OF(PARAM_NETWORK_ADDRESS, aucAddress) + sizeof(PARAM_NETWORK_ADDRESS); } #ifdef CONFIG_IPV6 for (i = 0; i < u4NumIPv6; i++) { prParamNetAddr->u2AddressLength = 6; prParamNetAddr->u2AddressType = PARAM_PROTOCOL_ID_TCP_IP; kalMemCopy(prParamNetAddr->aucAddress, ip6, sizeof(ip6)); prParamNetAddr = (P_PARAM_NETWORK_ADDRESS) ((ULONG) prParamNetAddr + sizeof(ip6)); u4Len += OFFSET_OF(PARAM_NETWORK_ADDRESS, aucAddress) + sizeof(ip6); } #endif ASSERT(u4Len <= sizeof(g_aucBufIpAddr)); rStatus = kalIoctl(prGlueInfo, wlanoidSetNetworkAddress, (PVOID) prParamNetAddrList, u4Len, FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen); } notify_suspend: DBGLOG(INIT, INFO, "IP: %d.%d.%d.%d, rStatus: %u\n", ip[0], ip[1], ip[2], ip[3], rStatus); /* if (rStatus != WLAN_STATUS_SUCCESS) */ wlanNotifyFwSuspend(prGlueInfo, TRUE); } void wlanHandleSystemResume(void) { struct net_device *prDev = NULL; P_GLUE_INFO_T prGlueInfo = NULL; WLAN_STATUS rStatus = WLAN_STATUS_FAILURE; UINT_8 ip[4] = { 0 }; #ifdef CONFIG_IPV6 UINT_8 ip6[16] = { 0 }; /* FIX ME: avoid to allocate large memory in stack */ #endif EVENT_AIS_BSS_INFO_T rParam; UINT_32 u4BufLen = 0; #if CFG_SUPPORT_DROP_MC_PACKET UINT_32 u4PacketFilter = 0; UINT_32 u4SetInfoLen = 0; #endif /* <1> Sanity check and acquire the net_device */ ASSERT(u4WlanDevNum <= CFG_MAX_WLAN_DEVICES); if (u4WlanDevNum == 0) { DBGLOG(INIT, ERROR, "wlanLateResume u4WlanDevNum==0 invalid!!\n"); return; } prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev; /* ASSERT(prDev); */ fgIsUnderSuspend = false; if (!prDev) { DBGLOG(INIT, INFO, "prDev == NULL!!!\n"); return; } /* <3> acquire the prGlueInfo */ prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); ASSERT(prGlueInfo); #if CFG_SUPPORT_DROP_MC_PACKET /* new filter should not include p2p mask */ #if CFG_ENABLE_WIFI_DIRECT_CFG_80211 u4PacketFilter = prGlueInfo->prAdapter->u4OsPacketFilter & (~PARAM_PACKET_FILTER_P2P_MASK); #endif if (kalIoctl(prGlueInfo, wlanoidSetCurrentPacketFilter, &u4PacketFilter, sizeof(u4PacketFilter), FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen) != WLAN_STATUS_SUCCESS) { DBGLOG(INIT, ERROR, "set packet filter failed.\n"); } #endif /* We will receive the event in rx, we will check if the status is the same in driver and FW, if not the same, trigger disconnetion procedure. */ kalMemZero(&rParam, sizeof(EVENT_AIS_BSS_INFO_T)); rStatus = kalIoctl(prGlueInfo, wlanoidQueryBSSInfo, &rParam, sizeof(EVENT_AIS_BSS_INFO_T), TRUE, TRUE, TRUE, FALSE, &u4BufLen); if (rStatus != WLAN_STATUS_SUCCESS) { DBGLOG(INIT, ERROR, "Query BSSinfo fail 0x%x!!\n", rStatus); } /* <2> get the IPv4 address */ if (!(prDev->ip_ptr) || !((struct in_device *)(prDev->ip_ptr))->ifa_list || !(&(((struct in_device *)(prDev->ip_ptr))->ifa_list->ifa_local))) { goto notify_resume; } /* <4> copy the IPv4 address */ kalMemCopy(ip, &(((struct in_device *)(prDev->ip_ptr))->ifa_list->ifa_local), sizeof(ip)); #ifdef CONFIG_IPV6 /* <5> get the IPv6 address */ if (!prDev || !(prDev->ip6_ptr) || !((struct in_device *)(prDev->ip6_ptr))->ifa_list || !(&(((struct in_device *)(prDev->ip6_ptr))->ifa_list->ifa_local))) { goto notify_resume; } /* <6> copy the IPv6 address */ kalMemCopy(ip6, &(((struct in_device *)(prDev->ip6_ptr))->ifa_list->ifa_local), sizeof(ip6)); DBGLOG(INIT, INFO, "ipv6 is %d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d\n", ip6[0], ip6[1], ip6[2], ip6[3], ip6[4], ip6[5], ip6[6], ip6[7], ip6[8], ip6[9], ip6[10], ip6[11], ip6[12], ip6[13], ip6[14], ip6[15] ); #endif /* <7> clear the ARP filter */ { UINT_32 u4SetInfoLen = 0; /* UINT_8 aucBuf[32] = {0}; */ UINT_32 u4Len = sizeof(PARAM_NETWORK_ADDRESS_LIST); P_PARAM_NETWORK_ADDRESS_LIST prParamNetAddrList = (P_PARAM_NETWORK_ADDRESS_LIST) g_aucBufIpAddr; /* aucBuf; */ kalMemZero(g_aucBufIpAddr, sizeof(g_aucBufIpAddr)); prParamNetAddrList->u4AddressCount = 0; prParamNetAddrList->u2AddressType = PARAM_PROTOCOL_ID_TCP_IP; ASSERT(u4Len <= sizeof(g_aucBufIpAddr /*aucBuf */)); rStatus = kalIoctl(prGlueInfo, wlanoidSetNetworkAddress, (PVOID) prParamNetAddrList, u4Len, FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen); } notify_resume: DBGLOG(INIT, INFO, "Query BSS result: %d %d %d, IP: %d.%d.%d.%d, rStatus: %u\n", rParam.eConnectionState, rParam.eCurrentOPMode, rParam.fgIsNetActive, ip[0], ip[1], ip[2], ip[3], rStatus); /* if (rStatus != WLAN_STATUS_SUCCESS) */ wlanNotifyFwSuspend(prGlueInfo, FALSE); } #endif /* ! CONFIG_X86 */ int set_p2p_mode_handler(struct net_device *netdev, PARAM_CUSTOM_P2P_SET_STRUCT_T p2pmode) { #if 0 P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(netdev)); PARAM_CUSTOM_P2P_SET_STRUCT_T rSetP2P; WLAN_STATUS rWlanStatus = WLAN_STATUS_SUCCESS; UINT_32 u4BufLen = 0; rSetP2P.u4Enable = p2pmode.u4Enable; rSetP2P.u4Mode = p2pmode.u4Mode; if (!rSetP2P.u4Enable) p2pNetUnregister(prGlueInfo, TRUE); rWlanStatus = kalIoctl(prGlueInfo, wlanoidSetP2pMode, (PVOID) &rSetP2P, sizeof(PARAM_CUSTOM_P2P_SET_STRUCT_T), FALSE, FALSE, TRUE, FALSE, &u4BufLen); DBGLOG(INIT, INFO, "ret = %d\n", rWlanStatus); if (rSetP2P.u4Enable) p2pNetRegister(prGlueInfo, TRUE); return 0; #else P_GLUE_INFO_T prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(netdev)); PARAM_CUSTOM_P2P_SET_STRUCT_T rSetP2P; WLAN_STATUS rWlanStatus = WLAN_STATUS_SUCCESS; BOOLEAN fgIsP2PEnding; UINT_32 u4BufLen = 0; GLUE_SPIN_LOCK_DECLARATION(); DBGLOG(INIT, INFO, "%u %u\n", (UINT_32) p2pmode.u4Enable, (UINT_32) p2pmode.u4Mode); /* avoid remove & p2p off command simultaneously */ GLUE_ACQUIRE_THE_SPIN_LOCK(&g_p2p_lock); fgIsP2PEnding = g_u4P2PEnding; g_u4P2POnOffing = 1; GLUE_RELEASE_THE_SPIN_LOCK(&g_p2p_lock); if (fgIsP2PEnding == 1) { /* skip the command if we are removing */ GLUE_ACQUIRE_THE_SPIN_LOCK(&g_p2p_lock); g_u4P2POnOffing = 0; GLUE_RELEASE_THE_SPIN_LOCK(&g_p2p_lock); return 0; } rSetP2P.u4Enable = p2pmode.u4Enable; rSetP2P.u4Mode = p2pmode.u4Mode; #if !CFG_SUPPORT_PERSIST_NETDEV if ((!rSetP2P.u4Enable) && (fgIsResetting == FALSE)) p2pNetUnregister(prGlueInfo, TRUE); #endif /* move out to caller to avoid kalIoctrl & suspend/resume deadlock problem ALPS00844864 */ /* Scenario: 1. System enters suspend/resume but not yet enter wlanearlysuspend() or wlanlateresume(); 2. System switches to do PRIV_CMD_P2P_MODE and execute kalIoctl() and get g_halt_sem then do glRegisterEarlySuspend() or glUnregisterEarlySuspend(); But system suspend/resume procedure is not yet finished so we suspend; 3. System switches back to do suspend/resume procedure and execute kalIoctl(). But driver does not yet release g_halt_sem so system suspend in wlanearlysuspend() or wlanlateresume(); ==> deadlock occurs. */ rWlanStatus = kalIoctl(prGlueInfo, wlanoidSetP2pMode, (PVOID) &rSetP2P,/* pu4IntBuf[0]is used as input SubCmd */ sizeof(PARAM_CUSTOM_P2P_SET_STRUCT_T), FALSE, FALSE, TRUE, FALSE, &u4BufLen); #if !CFG_SUPPORT_PERSIST_NETDEV /* Need to check fgIsP2PRegistered, in case of whole chip reset. * in this case, kalIOCTL return success always, * and prGlueInfo->prP2pInfo may be NULL */ if ((rSetP2P.u4Enable) && (prGlueInfo->prAdapter->fgIsP2PRegistered) && (fgIsResetting == FALSE)) p2pNetRegister(prGlueInfo, TRUE); #endif GLUE_ACQUIRE_THE_SPIN_LOCK(&g_p2p_lock); g_u4P2POnOffing = 0; GLUE_RELEASE_THE_SPIN_LOCK(&g_p2p_lock); return 0; #endif } static void set_dbg_level_handler(unsigned char dbg_lvl[DBG_MODULE_NUM]) { kalMemCopy(aucDebugModule, dbg_lvl, sizeof(aucDebugModule)); kalPrint("[wlan] change debug level"); } /*----------------------------------------------------------------------------*/ /*! * \brief Wlan probe function. This function probes and initializes the device. * * \param[in] pvData data passed by bus driver init function * _HIF_EHPI: NULL * _HIF_SDIO: sdio bus driver handle * * \retval 0 Success * \retval negative value Failed */ /*----------------------------------------------------------------------------*/ static INT_32 wlanProbe(PVOID pvData) { struct wireless_dev *prWdev = NULL; enum probe_fail_reason { BUS_INIT_FAIL, NET_CREATE_FAIL, BUS_SET_IRQ_FAIL, ADAPTER_START_FAIL, NET_REGISTER_FAIL, PROC_INIT_FAIL, FAIL_REASON_NUM } eFailReason; P_WLANDEV_INFO_T prWlandevInfo = NULL; INT_32 i4DevIdx = 0; P_GLUE_INFO_T prGlueInfo = NULL; P_ADAPTER_T prAdapter = NULL; INT_32 i4Status = 0; BOOLEAN bRet = FALSE; eFailReason = FAIL_REASON_NUM; do { /* 4 <1> Initialize the IO port of the interface */ /* GeorgeKuo: pData has different meaning for _HIF_XXX: * _HIF_EHPI: pointer to memory base variable, which will be * initialized by glBusInit(). * _HIF_SDIO: bus driver handle */ bRet = glBusInit(pvData); wlanDebugInit(); /* Cannot get IO address from interface */ if (FALSE == bRet) { DBGLOG(INIT, ERROR, KERN_ALERT "wlanProbe: glBusInit() fail\n"); i4Status = -EIO; eFailReason = BUS_INIT_FAIL; break; } /* 4 <2> Create network device, Adapter, KalInfo, prDevHandler(netdev) */ prWdev = wlanNetCreate(pvData); if (prWdev == NULL) { DBGLOG(INIT, ERROR, "wlanProbe: No memory for dev and its private\n"); i4Status = -ENOMEM; eFailReason = NET_CREATE_FAIL; break; } /* 4 <2.5> Set the ioaddr to HIF Info */ prGlueInfo = (P_GLUE_INFO_T) wiphy_priv(prWdev->wiphy); gPrDev = prGlueInfo->prDevHandler; /* 4 <4> Setup IRQ */ prWlandevInfo = &arWlanDevInfo[i4DevIdx]; i4Status = glBusSetIrq(prWdev->netdev, NULL, *((P_GLUE_INFO_T *) netdev_priv(prWdev->netdev))); if (i4Status != WLAN_STATUS_SUCCESS) { DBGLOG(INIT, ERROR, "wlanProbe: Set IRQ error\n"); eFailReason = BUS_SET_IRQ_FAIL; break; } prGlueInfo->i4DevIdx = i4DevIdx; prAdapter = prGlueInfo->prAdapter; prGlueInfo->u4ReadyFlag = 0; #if CFG_TCP_IP_CHKSUM_OFFLOAD prAdapter->u4CSUMFlags = (CSUM_OFFLOAD_EN_TX_TCP | CSUM_OFFLOAD_EN_TX_UDP | CSUM_OFFLOAD_EN_TX_IP); #endif #if CFG_SUPPORT_CFG_FILE #ifdef ENABLED_IN_ENGUSERDEBUG { PUINT_8 pucConfigBuf; UINT_32 u4ConfigReadLen; wlanCfgInit(prAdapter, NULL, 0, 0); pucConfigBuf = (PUINT_8) kalMemAlloc(WLAN_CFG_FILE_BUF_SIZE, VIR_MEM_TYPE); u4ConfigReadLen = 0; DBGLOG(INIT, LOUD, "CFG_FILE: Read File...\n"); if (pucConfigBuf) { kalMemZero(pucConfigBuf, WLAN_CFG_FILE_BUF_SIZE); if (kalReadToFile("/data/misc/wifi.cfg", pucConfigBuf, WLAN_CFG_FILE_BUF_SIZE, &u4ConfigReadLen) == 0) { DBGLOG(INIT, LOUD, "CFG_FILE: Read /data/misc/wifi.cfg\n"); } else if (kalReadToFile("/data/misc/wifi/wifi.cfg", pucConfigBuf, WLAN_CFG_FILE_BUF_SIZE, &u4ConfigReadLen) == 0) { DBGLOG(INIT, LOUD, "CFG_FILE: Read /data/misc/wifi/wifi.cfg\n"); } else if (kalReadToFile("/etc/firmware/wifi.cfg", pucConfigBuf, WLAN_CFG_FILE_BUF_SIZE, &u4ConfigReadLen) == 0) { DBGLOG(INIT, LOUD, "CFG_FILE: Read /etc/firmware/wifi.cfg\n"); } if (pucConfigBuf[0] != '\0' && u4ConfigReadLen > 0) wlanCfgInit(prAdapter, pucConfigBuf, u4ConfigReadLen, 0); kalMemFree(pucConfigBuf, VIR_MEM_TYPE, WLAN_CFG_FILE_BUF_SIZE); } /* pucConfigBuf */ } #endif #endif /* 4 <5> Start Device */ /* */ #if CFG_ENABLE_FW_DOWNLOAD DBGLOG(INIT, TRACE, "start to download firmware...\n"); /* before start adapter, we need to open and load firmware */ { UINT_32 u4FwSize = 0; PVOID prFwBuffer = NULL; P_REG_INFO_T prRegInfo = &prGlueInfo->rRegInfo; /* P_REG_INFO_T prRegInfo = (P_REG_INFO_T) kmalloc(sizeof(REG_INFO_T), GFP_KERNEL); */ kalMemSet(prRegInfo, 0, sizeof(REG_INFO_T)); prRegInfo->u4StartAddress = CFG_FW_START_ADDRESS; prRegInfo->u4LoadAddress = CFG_FW_LOAD_ADDRESS; /* Load NVRAM content to REG_INFO_T */ glLoadNvram(prGlueInfo, prRegInfo); #if CFG_SUPPORT_CFG_FILE #ifdef ENABLED_IN_ENGUSERDEBUG wlanCfgApply(prAdapter); #endif #endif /* kalMemCopy(&prGlueInfo->rRegInfo, prRegInfo, sizeof(REG_INFO_T)); */ prRegInfo->u4PowerMode = CFG_INIT_POWER_SAVE_PROF; prRegInfo->fgEnArpFilter = TRUE; if (kalFirmwareImageMapping(prGlueInfo, &prFwBuffer, &u4FwSize) == NULL) { i4Status = -EIO; DBGLOG(INIT, ERROR, "kalFirmwareImageMapping fail!\n"); goto bailout; } else { if (wlanAdapterStart(prAdapter, prRegInfo, prFwBuffer, u4FwSize) != WLAN_STATUS_SUCCESS) { i4Status = -EIO; } } kalFirmwareImageUnmapping(prGlueInfo, NULL, prFwBuffer); bailout: /* kfree(prRegInfo); */ DBGLOG(INIT, TRACE, "download firmware status = %d\n", i4Status); if (i4Status < 0) { GL_HIF_INFO_T *HifInfo; UINT_32 u4FwCnt; DBGLOG(INIT, WARN, "CONNSYS FW CPUINFO:\n"); HifInfo = &prAdapter->prGlueInfo->rHifInfo; for (u4FwCnt = 0; u4FwCnt < 16; u4FwCnt++) DBGLOG(INIT, WARN, "0x%08x ", MCU_REG_READL(HifInfo, CONN_MCU_CPUPCR)); /* CONSYS_REG_READ(CONSYS_CPUPCR_REG) */ /* dump HIF/DMA registers, if fgIsBusAccessFailed is FALSE, otherwise, */ /* dump HIF register may be hung */ if (!fgIsBusAccessFailed) HifRegDump(prGlueInfo->prAdapter); /* if (prGlueInfo->rHifInfo.DmaOps->DmaRegDump != NULL) */ /* prGlueInfo->rHifInfo.DmaOps->DmaRegDump(&prGlueInfo->rHifInfo); */ eFailReason = ADAPTER_START_FAIL; break; } } #else /* P_REG_INFO_T prRegInfo = (P_REG_INFO_T) kmalloc(sizeof(REG_INFO_T), GFP_KERNEL); */ kalMemSet(&prGlueInfo->rRegInfo, 0, sizeof(REG_INFO_T)); P_REG_INFO_T prRegInfo = &prGlueInfo->rRegInfo; /* Load NVRAM content to REG_INFO_T */ glLoadNvram(prGlueInfo, prRegInfo); prRegInfo->u4PowerMode = CFG_INIT_POWER_SAVE_PROF; if (wlanAdapterStart(prAdapter, prRegInfo, NULL, 0) != WLAN_STATUS_SUCCESS) { i4Status = -EIO; eFailReason = ADAPTER_START_FAIL; break; } #endif if (FALSE == prAdapter->fgEnable5GBand) prWdev->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; else prWdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &mtk_band_5ghz; prGlueInfo->main_thread = kthread_run(tx_thread, prGlueInfo->prDevHandler, "tx_thread"); kalSetHalted(FALSE); #if CFG_SUPPORT_ROAMING_ENC /* adjust roaming threshold */ { WLAN_STATUS rStatus = WLAN_STATUS_FAILURE; CMD_ROAMING_INFO_T rRoamingInfo; UINT_32 u4SetInfoLen = 0; prAdapter->fgIsRoamingEncEnabled = TRUE; /* suggestion from Tsaiyuan.Hsu */ kalMemZero(&rRoamingInfo, sizeof(CMD_ROAMING_INFO_T)); rRoamingInfo.fgIsFastRoamingApplied = TRUE; DBGLOG(INIT, TRACE, "Enable roaming enhance function\n"); rStatus = kalIoctl(prGlueInfo, wlanoidSetRoamingInfo, &rRoamingInfo, sizeof(rRoamingInfo), TRUE, TRUE, TRUE, FALSE, &u4SetInfoLen); if (rStatus != WLAN_STATUS_SUCCESS) DBGLOG(INIT, ERROR, "set roaming advance info fail 0x%x\n", rStatus); } #endif /* CFG_SUPPORT_ROAMING_ENC */ #if (CFG_SUPPORT_TXR_ENC == 1) /* adjust tx rate switch threshold */ rlmTxRateEnhanceConfig(prGlueInfo->prAdapter); #endif /* CFG_SUPPORT_TXR_ENC */ /* set MAC address */ { WLAN_STATUS rStatus = WLAN_STATUS_FAILURE; struct sockaddr MacAddr; UINT_32 u4SetInfoLen = 0; kalMemZero(MacAddr.sa_data, sizeof(MacAddr.sa_data)); rStatus = kalIoctl(prGlueInfo, wlanoidQueryCurrentAddr, &MacAddr.sa_data, PARAM_MAC_ADDR_LEN, TRUE, TRUE, TRUE, FALSE, &u4SetInfoLen); if (rStatus != WLAN_STATUS_SUCCESS) { DBGLOG(INIT, WARN, "set MAC addr fail 0x%x\n", rStatus); prGlueInfo->u4ReadyFlag = 0; } else { ether_addr_copy(prGlueInfo->prDevHandler->dev_addr, (const u8 *)&(MacAddr.sa_data)); ether_addr_copy(prGlueInfo->prDevHandler->perm_addr, prGlueInfo->prDevHandler->dev_addr); /* card is ready */ prGlueInfo->u4ReadyFlag = 1; #if CFG_SHOW_MACADDR_SOURCE DBGLOG(INIT, INFO, "MAC address: %pM ", (&MacAddr.sa_data)); #endif } } #ifdef FW_CFG_SUPPORT { if (wlanFwArrayCfg(prAdapter) != WLAN_STATUS_FAILURE) DBGLOG(INIT, INFO, "FW Array Cfg done!"); } #ifdef ENABLED_IN_ENGUSERDEBUG { if (wlanFwFileCfg(prAdapter) != WLAN_STATUS_FAILURE) DBGLOG(INIT, INFO, "FW File Cfg done!"); } #endif #endif #if CFG_TCP_IP_CHKSUM_OFFLOAD /* set HW checksum offload */ { WLAN_STATUS rStatus = WLAN_STATUS_FAILURE; UINT_32 u4CSUMFlags = CSUM_OFFLOAD_EN_ALL; UINT_32 u4SetInfoLen = 0; rStatus = kalIoctl(prGlueInfo, wlanoidSetCSUMOffload, (PVOID) &u4CSUMFlags, sizeof(UINT_32), FALSE, FALSE, TRUE, FALSE, &u4SetInfoLen); if (rStatus != WLAN_STATUS_SUCCESS) DBGLOG(INIT, WARN, "set HW checksum offload fail 0x%x\n", rStatus); } #endif /* 4 <3> Register the card */ DBGLOG(INIT, TRACE, "wlanNetRegister...\n"); i4DevIdx = wlanNetRegister(prWdev); if (i4DevIdx < 0) { i4Status = -ENXIO; DBGLOG(INIT, ERROR, "wlanProbe: Cannot register the net_device context to the kernel\n"); eFailReason = NET_REGISTER_FAIL; break; } wlanRegisterNotifier(); /* 4 <6> Initialize /proc filesystem */ #ifdef WLAN_INCLUDE_PROC DBGLOG(INIT, TRACE, "init procfs...\n"); i4Status = procCreateFsEntry(prGlueInfo); if (i4Status < 0) { DBGLOG(INIT, ERROR, "wlanProbe: init procfs failed\n"); eFailReason = PROC_INIT_FAIL; break; } #endif /* WLAN_INCLUDE_PROC */ #ifdef FW_CFG_SUPPORT i4Status = cfgCreateProcEntry(prGlueInfo); if (i4Status < 0) { DBGLOG(INIT, ERROR, "fw cfg proc failed\n"); break; } #endif #if CFG_ENABLE_BT_OVER_WIFI prGlueInfo->rBowInfo.fgIsNetRegistered = FALSE; prGlueInfo->rBowInfo.fgIsRegistered = FALSE; glRegisterAmpc(prGlueInfo); #endif #if CFG_ENABLE_WIFI_DIRECT DBGLOG(INIT, TRACE, "wlanSubModInit...\n"); /* wlan is launched */ prGlueInfo->prAdapter->fgIsWlanLaunched = TRUE; /* if p2p module is inserted, notify tx_thread to init p2p network */ if (rSubModHandler[P2P_MODULE].subModInit) wlanSubModInit(prGlueInfo); /* register set_p2p_mode handler to mtk_wmt_wifi */ register_set_p2p_mode_handler(set_p2p_mode_handler); #endif #if CFG_SPM_WORKAROUND_FOR_HOTSPOT if (glIsChipNeedWakelock(prGlueInfo)) KAL_WAKE_LOCK_INIT(prGlueInfo->prAdapter, &prGlueInfo->prAdapter->rApWakeLock, "WLAN AP"); #endif } while (FALSE); if (i4Status != WLAN_STATUS_SUCCESS) { switch (eFailReason) { case PROC_INIT_FAIL: wlanNetUnregister(prWdev); set_bit(GLUE_FLAG_HALT_BIT, &prGlueInfo->ulFlag); /* wake up main thread */ wake_up_interruptible(&prGlueInfo->waitq); /* wait main thread stops */ wait_for_completion_interruptible(&prGlueInfo->rHaltComp); KAL_WAKE_LOCK_DESTROY(prAdapter, &prAdapter->rTxThreadWakeLock); wlanAdapterStop(prAdapter); glBusFreeIrq(prWdev->netdev, *((P_GLUE_INFO_T *) netdev_priv(prWdev->netdev))); KAL_WAKE_LOCK_DESTROY(prAdapter, &prGlueInfo->rAhbIsrWakeLock); wlanNetDestroy(prWdev); break; case NET_REGISTER_FAIL: set_bit(GLUE_FLAG_HALT_BIT, &prGlueInfo->ulFlag); /* wake up main thread */ wake_up_interruptible(&prGlueInfo->waitq); /* wait main thread stops */ wait_for_completion_interruptible(&prGlueInfo->rHaltComp); KAL_WAKE_LOCK_DESTROY(prAdapter, &prAdapter->rTxThreadWakeLock); wlanAdapterStop(prAdapter); glBusFreeIrq(prWdev->netdev, *((P_GLUE_INFO_T *) netdev_priv(prWdev->netdev))); KAL_WAKE_LOCK_DESTROY(prAdapter, &prGlueInfo->rAhbIsrWakeLock); wlanNetDestroy(prWdev); break; case ADAPTER_START_FAIL: glBusFreeIrq(prWdev->netdev, *((P_GLUE_INFO_T *) netdev_priv(prWdev->netdev))); KAL_WAKE_LOCK_DESTROY(prAdapter, &prGlueInfo->rAhbIsrWakeLock); wlanNetDestroy(prWdev); break; case BUS_SET_IRQ_FAIL: KAL_WAKE_LOCK_DESTROY(prAdapter, &prGlueInfo->rAhbIsrWakeLock); wlanNetDestroy(prWdev); break; case NET_CREATE_FAIL: break; case BUS_INIT_FAIL: break; default: break; } } #if CFG_ENABLE_WIFI_DIRECT { GLUE_SPIN_LOCK_DECLARATION(); GLUE_ACQUIRE_THE_SPIN_LOCK(&g_p2p_lock); g_u4P2PEnding = 0; GLUE_RELEASE_THE_SPIN_LOCK(&g_p2p_lock); } #endif #if CFG_SUPPORT_AGPS_ASSIST if (i4Status == WLAN_STATUS_SUCCESS) kalIndicateAgpsNotify(prAdapter, AGPS_EVENT_WLAN_ON, NULL, 0); #endif #if (CFG_SUPPORT_MET_PROFILING == 1) { int iMetInitRet = WLAN_STATUS_FAILURE; if (i4Status == WLAN_STATUS_SUCCESS) { DBGLOG(INIT, TRACE, "init MET procfs...\n"); iMetInitRet = kalMetInitProcfs(prGlueInfo); if (iMetInitRet < 0) DBGLOG(INIT, ERROR, "wlanProbe: init MET procfs failed\n"); } } #endif if (i4Status == WLAN_STATUS_SUCCESS) { /*Init performance monitor structure */ kalPerMonInit(prGlueInfo); /* probe ok */ DBGLOG(INIT, TRACE, "wlanProbe ok\n"); } else { /* we don't care the return value of mtk_wcn_set_connsys_power_off_flag, * because even this function returns * error, we can also call core dump but only core dump failed. */ if (g_IsNeedDoChipReset) mtk_wcn_set_connsys_power_off_flag(0); /* probe failed */ DBGLOG(INIT, ERROR, "wlanProbe failed\n"); } return i4Status; } /* end of wlanProbe() */ /*----------------------------------------------------------------------------*/ /*! * \brief A method to stop driver operation and release all resources. Following * this call, no frame should go up or down through this interface. * * \return (none) */ /*----------------------------------------------------------------------------*/ static VOID wlanRemove(VOID) { #define KAL_WLAN_REMOVE_TIMEOUT_MSEC 3000 struct net_device *prDev = NULL; P_WLANDEV_INFO_T prWlandevInfo = NULL; P_GLUE_INFO_T prGlueInfo = NULL; P_ADAPTER_T prAdapter = NULL; DBGLOG(INIT, LOUD, "Remove wlan!\n"); /* 4 <0> Sanity check */ ASSERT(u4WlanDevNum <= CFG_MAX_WLAN_DEVICES); if (0 == u4WlanDevNum) { DBGLOG(INIT, ERROR, "0 == u4WlanDevNum\n"); return; } /* unregister set_p2p_mode handler to mtk_wmt_wifi */ register_set_p2p_mode_handler(NULL); prDev = arWlanDevInfo[u4WlanDevNum - 1].prDev; prWlandevInfo = &arWlanDevInfo[u4WlanDevNum - 1]; ASSERT(prDev); if (NULL == prDev) { DBGLOG(INIT, ERROR, "NULL == prDev\n"); return; } prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prDev)); ASSERT(prGlueInfo); if (NULL == prGlueInfo) { DBGLOG(INIT, ERROR, "NULL == prGlueInfo\n"); free_netdev(prDev); return; } kalPerMonDestroy(prGlueInfo); /* 4 <3> Remove /proc filesystem. */ #ifdef FW_CFG_SUPPORT cfgRemoveProcEntry(); #endif #ifdef WLAN_INCLUDE_PROC procRemoveProcfs(); #endif /* WLAN_INCLUDE_PROC */ #if CFG_ENABLE_WIFI_DIRECT /* avoid remove & p2p off command simultaneously */ { BOOLEAN fgIsP2POnOffing; GLUE_SPIN_LOCK_DECLARATION(); GLUE_ACQUIRE_THE_SPIN_LOCK(&g_p2p_lock); g_u4P2PEnding = 1; fgIsP2POnOffing = g_u4P2POnOffing; GLUE_RELEASE_THE_SPIN_LOCK(&g_p2p_lock); DBGLOG(INIT, TRACE, "waiting for fgIsP2POnOffing...\n"); /* History: cannot use down() here, sometimes we cannot come back here */ /* waiting for p2p off command finishes, we cannot skip the remove */ while (1) { if (fgIsP2POnOffing == 0) break; GLUE_ACQUIRE_THE_SPIN_LOCK(&g_p2p_lock); fgIsP2POnOffing = g_u4P2POnOffing; GLUE_RELEASE_THE_SPIN_LOCK(&g_p2p_lock); } } #endif #if CFG_ENABLE_BT_OVER_WIFI if (prGlueInfo->rBowInfo.fgIsNetRegistered) { bowNotifyAllLinkDisconnected(prGlueInfo->prAdapter); /* wait 300ms for BoW module to send deauth */ kalMsleep(300); } #endif /* 4 <1> Stopping handling interrupt and free IRQ */ DBGLOG(INIT, TRACE, "free IRQ...\n"); glBusFreeIrq(prDev, *((P_GLUE_INFO_T *) netdev_priv(prDev))); kalMemSet(&(prGlueInfo->prAdapter->rWlanInfo), 0, sizeof(WLAN_INFO_T)); kalSetHalted(TRUE); /* before flush_delayed_work() */ if (fgIsWorkMcStart == TRUE) { DBGLOG(INIT, TRACE, "flush_delayed_work...\n"); flush_delayed_work(&workq); /* flush_delayed_work_sync is deprecated */ } flush_delayed_work(&sched_workq); DBGLOG(INIT, INFO, "down g_halt_sem...\n"); kalHaltLock(KAL_WLAN_REMOVE_TIMEOUT_MSEC); #if CFG_SPM_WORKAROUND_FOR_HOTSPOT if (glIsChipNeedWakelock(prGlueInfo)) KAL_WAKE_LOCK_DESTROY(prGlueInfo->prAdapter, &prGlueInfo->prAdapter->rApWakeLock); #endif /* flush_delayed_work_sync(&workq); */ /* flush_delayed_work(&workq); */ /* flush_delayed_work_sync is deprecated */ /* 4 <2> Mark HALT, notify main thread to stop, and clean up queued requests */ /* prGlueInfo->u4Flag |= GLUE_FLAG_HALT; */ set_bit(GLUE_FLAG_HALT_BIT, &prGlueInfo->ulFlag); DBGLOG(INIT, TRACE, "waiting for tx_thread stop...\n"); /* wake up main thread */ wake_up_interruptible(&prGlueInfo->waitq); DBGLOG(INIT, TRACE, "wait_for_completion_interruptible\n"); /* wait main thread stops */ wait_for_completion_interruptible(&prGlueInfo->rHaltComp); DBGLOG(INIT, TRACE, "mtk_sdiod stopped\n"); KAL_WAKE_LOCK_DESTROY(prGlueInfo->prAdapter, &prGlueInfo->prAdapter->rTxThreadWakeLock); KAL_WAKE_LOCK_DESTROY(prGlueInfo->prAdapter, &prGlueInfo->rAhbIsrWakeLock); /* prGlueInfo->rHifInfo.main_thread = NULL; */ prGlueInfo->main_thread = NULL; #if CFG_ENABLE_BT_OVER_WIFI if (prGlueInfo->rBowInfo.fgIsRegistered) glUnregisterAmpc(prGlueInfo); #endif #if (CFG_SUPPORT_MET_PROFILING == 1) kalMetRemoveProcfs(); #endif /* Force to do DMA reset */ DBGLOG(INIT, TRACE, "glResetHif\n"); glResetHif(prGlueInfo); /* 4 <4> wlanAdapterStop */ prAdapter = prGlueInfo->prAdapter; #if CFG_SUPPORT_AGPS_ASSIST kalIndicateAgpsNotify(prAdapter, AGPS_EVENT_WLAN_OFF, NULL, 0); #endif wlanAdapterStop(prAdapter); DBGLOG(INIT, TRACE, "Number of Stalled Packets = %d\n", prGlueInfo->i4TxPendingFrameNum); #if CFG_ENABLE_WIFI_DIRECT prGlueInfo->prAdapter->fgIsWlanLaunched = FALSE; if (prGlueInfo->prAdapter->fgIsP2PRegistered) { DBGLOG(INIT, TRACE, "p2pNetUnregister...\n"); #if !CFG_SUPPORT_PERSIST_NETDEV p2pNetUnregister(prGlueInfo, FALSE); #endif DBGLOG(INIT, INFO, "p2pRemove...\n"); p2pRemove(prGlueInfo); } #endif /* 4 <5> Release the Bus */ glBusRelease(prDev); kalHaltUnlock(); wlanDebugUninit(); /* 4 <6> Unregister the card */ wlanNetUnregister(prDev->ieee80211_ptr); /* 4 <7> Destroy the device */ wlanNetDestroy(prDev->ieee80211_ptr); prDev = NULL; DBGLOG(INIT, LOUD, "wlanUnregisterNotifier...\n"); wlanUnregisterNotifier(); DBGLOG(INIT, INFO, "wlanRemove ok\n"); } /* end of wlanRemove() */ /*----------------------------------------------------------------------------*/ /*! * \brief Driver entry point when the driver is configured as a Linux Module, and * is called once at module load time, by the user-level modutils * application: insmod or modprobe. * * \retval 0 Success */ /*----------------------------------------------------------------------------*/ /* 1 Module Entry Point */ static int initWlan(void) { int ret = 0, i; #if DBG for (i = 0; i < DBG_MODULE_NUM; i++) aucDebugModule[i] = DBG_CLASS_MASK; /* enable all */ #else /* Initial debug level is D1 */ for (i = 0; i < DBG_MODULE_NUM; i++) aucDebugModule[i] = DBG_CLASS_ERROR | DBG_CLASS_WARN | DBG_CLASS_INFO | DBG_CLASS_STATE; #endif /* DBG */ DBGLOG(INIT, INFO, "initWlan\n"); spin_lock_init(&g_p2p_lock); /* memory pre-allocation */ kalInitIOBuffer(); procInitFs(); createWirelessDevice(); if (gprWdev) glP2pCreateWirelessDevice((P_GLUE_INFO_T) wiphy_priv(gprWdev->wiphy)); ret = ((glRegisterBus(wlanProbe, wlanRemove) == WLAN_STATUS_SUCCESS) ? 0 : -EIO); if (ret == -EIO) { kalUninitIOBuffer(); return ret; } #if (CFG_CHIP_RESET_SUPPORT) glResetInit(); #endif /* register set_dbg_level handler to mtk_wmt_wifi */ register_set_dbg_level_handler(set_dbg_level_handler); /* Register framebuffer notifier client*/ kalFbNotifierReg((P_GLUE_INFO_T) wiphy_priv(gprWdev->wiphy)); /* Set the initial DEBUG CLASS of each module */ return ret; } /* end of initWlan() */ /*----------------------------------------------------------------------------*/ /*! * \brief Driver exit point when the driver as a Linux Module is removed. Called * at module unload time, by the user level modutils application: rmmod. * This is our last chance to clean up after ourselves. * * \return (none) */ /*----------------------------------------------------------------------------*/ /* 1 Module Leave Point */ static VOID exitWlan(void) { DBGLOG(INIT, INFO, "exitWlan\n"); /* Unregister framebuffer notifier client*/ kalFbNotifierUnReg(); /* unregister set_dbg_level handler to mtk_wmt_wifi */ register_set_dbg_level_handler(NULL); #if CFG_CHIP_RESET_SUPPORT glResetUninit(); #endif destroyWirelessDevice(); glP2pDestroyWirelessDevice(); glUnregisterBus(wlanRemove); /* free pre-allocated memory */ kalUninitIOBuffer(); DBGLOG(INIT, INFO, "exitWlan\n"); procUninitProcFs(); } /* end of exitWlan() */ #ifdef MTK_WCN_BUILT_IN_DRIVER int mtk_wcn_wlan_gen2_init(void) { return initWlan(); } EXPORT_SYMBOL(mtk_wcn_wlan_gen2_init); void mtk_wcn_wlan_gen2_exit(void) { return exitWlan(); } EXPORT_SYMBOL(mtk_wcn_wlan_gen2_exit); #else module_init(initWlan); module_exit(exitWlan); #endif