Project import generated by Copybara. GitOrigin-RevId: c415cd6adaf576dac916f3adff080fcd4ac23400
diff --git a/bt_sd8897/Makefile b/bt_sd8897/Makefile new file mode 100644 index 0000000..444c953 --- /dev/null +++ b/bt_sd8897/Makefile
@@ -0,0 +1,169 @@ +# File: Makefile +# Copyright (C) 2007-2018, Marvell International Ltd. +# + +CC= $(CROSS_COMPILE)gcc +LD= $(CROSS_COMPILE)ld + +BACKUP= /root/backup +YMD= `date +%Y%m%d%H%M` + +############################################################################# +# Configuration Options +############################################################################# + +# Debug Option +# DEBUG LEVEL n/1/2: +# n: NO DEBUG +# 1: PRINTM(MSG,...), PRINTM(FATAL,...), PRINTM(WARN,...) and PRINTM(INFO,...) +# 2: All PRINTM() +CONFIG_DEBUG=1 + + +# SDIO suspend/resume +CONFIG_SDIO_SUSPEND_RESUME=y + + +CONFIG_BLE_WAKEUP=y + +############################################################################# +# Select Platform Tools +############################################################################# + +MODEXT = ko + +ifeq ($(CONFIG_64BIT), y) + EXTRA_CFLAGS += -DMBT_64BIT +endif + +ifeq ($(CONFIG_T50), y) + EXTRA_CFLAGS += -DT50 + EXTRA_CFLAGS += -DT40 + EXTRA_CFLAGS += -DT3T +endif + +ifeq ($(CONFIG_BLE_WAKEUP), y) + EXTRA_CFLAGS += -DBLE_WAKEUP +endif + + + + + + +ARCH ?= arm +KERNELDIR ?= /usr/src/arm/linux-4.9.70-bg4ct +CROSS_COMPILE ?= /usr/local/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- + +EXTRA_CFLAGS += -I$(KERNELDIR)/include + +EXTRA_CFLAGS += -I$(M)/../mbtchar_src +EXTRA_CFLAGS += -I$(M)/bt +LD += -S + +#ifdef SD8xxx +BINDIR = ../bin_sd8xxx_btchar +#endif + + +############################################################################# +# Compiler Flags +############################################################################# + EXTRA_CFLAGS += -DFPNUM='"28"' + +ifeq ($(CONFIG_DEBUG),1) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 +endif + +ifeq ($(CONFIG_DEBUG),2) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 + EXTRA_CFLAGS += -DDEBUG_LEVEL2 + DBG= -dbg +endif + +ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y) + EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME +endif + +############################################################################# +# Make Targets +############################################################################# + +ifneq ($(KERNELRELEASE),) + +BTOBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o + +BTOBJS += bt/bt_init.o + +obj-m := mbt8897.o +mbt8897-objs := $(BTOBJS) + + + +# Otherwise we were called directly from the command line; invoke the kernel build system. +else +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules +endif + +############################################################### + +export CC LD EXTRA_CFLAGS KERNELDIR + +.PHONY: app/fm_app clean distclean + +app/fm_app: + $(MAKE) -C $@ + +echo: + +build: echo default + + @if [ ! -d $(BINDIR) ]; then \ + mkdir $(BINDIR); \ + fi + + cp -f mbt8xxx.$(MODEXT) $(BINDIR)/mbt8xxx$(DBG).$(MODEXT) + cp -r config $(BINDIR) + + + + + + cp -f README $(BINDIR) + + $(MAKE) -C app/fm_app $@ INSTALLDIR=$(BINDIR); + cp -f app/fm_app/fmapp $(BINDIR); + +clean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name "*.symvers" -exec rm {} \; + -find . -name "modules.order" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + $(MAKE) -C app/fm_app $@ + +install: default + +distclean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.orig" -exec rm {} \; + -find . -name "*.swp" -exec rm {} \; + -find . -name "*.*~" -exec rm {} \; + -find . -name "*~" -exec rm {} \; + -find . -name "*.d" -exec rm {} \; + -find . -name "*.a" -exec rm {} \; + -find . -name "tags" -exec rm {} \; + -find . -name ".*" -exec rm -rf 2> /dev/null \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + $(MAKE) -C app/fm_app $@ +# End of file;
diff --git a/bt_sd8897/README b/bt_sd8897/README new file mode 100644 index 0000000..ed48a72 --- /dev/null +++ b/bt_sd8897/README
@@ -0,0 +1,224 @@ +=============================================================================== + U S E R M A N U A L + + Copyright (C) 2003-2016, Marvell International Ltd. + + This software file (the "File") is distributed by Marvell International + Ltd. under the terms of the GNU General Public License Version 2, June 1991 + (the "License"). You may use, redistribute and/or modify this File in + accordance with the terms and conditions of the License, a copy of which + is available along with the File in the gpl.txt file or by writing to + the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + ARE EXPRESSLY DISCLAIMED. The License provides additional details about + this warranty disclaimer. + +=============================================================================== + +1) FOR DRIVER BUILD + + Goto source code directory mbtc_src/. + make [clean] build + The driver binary can be found in ../bin_xxxx_btchar directory. + +2) FOR DRIVER INSTALL + + a) Copy sd8787.bin | ... to /lib/firmware/mrvl/ directory, + create the directory if it doesn't exist. + b) Install bluetooth driver, + insmod bt8787.ko | mbt8787.ko | ... [fw_name=mrvl/sd8xxx.bin] + c) Uninstall bluetooth driver, + rmmod bt8xxx | mbt8xxx + + There are some other parameters for debugging purpose etc. Use modinfo to check details. + The bit settings of drv_mode are, + Bit 0: BT/AMP/BLE + Bit 1: FM + Bit 2: NFC + mbt_drvdbg=<bit mask of driver debug message control> + psmode=1|0 <enable PS mode (default) | disable PS mode> + dts_enable=1|0 <Disable DTS | Enable DTS (default)> + bt_name=<BT interface name> + fm_name=<FM interface name> + nfc_name=<NFC interface name> + debug_intf=1|0 <Enable debug interface (default) | Disable debug interface> + debug_name=<Debug interface name> + mbt_pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend> + fw=0|other <Skip firmware download | Download firmware (default)> + bt_req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API> + init_cfg=<init config (MAC addresses, registers etc.) file name> + e.g. copy bt_init_cfg.conf to firmware directory, init_cfg=mrvl/bt_init_cfg.conf + bt_mac=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)> + cal_cfg=<BT CAL data config file name> + e.g. copy bt_cal_data.conf to firmware directory, cal_cfg=mrvl/bt_cal_data.conf + cal_cfg_ext=<CAL data config file name> + e.g. copy cal_data.conf to firmware directory, cal_cfg_ext=mrvl/cal_data.conf + mbt_gpio_pin=<GPIO pin to interrupt host. 0xFFFF: disable GPIO interrupt mode; Others: GPIO pin assigned to generate pulse to host.> + + btindrst = Independent reset configuration; high byte:GPIOpin;low byte:MODE + + <MODE> : + 0x00 : disable independent reset + 0x01 : enable out-band gpio independent reset. + 0x02 : enable in-band independent reset + <GPIOpin> : + 0xFF : default GPIO pins will be used. Currently for BT it is GPIO[15]. + 0xXX : specified GPIO pin number will be used for out-band reset. + + Example: + btindrst=0x0e01 : outband-reset, gpio pin 14 + btindrst=0xff01 : outband-reset, use firmware default GPIO pin + + Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used + for module parameters. + insmod mbt8xxx.ko "<para1> <para2> ..." + +3) cat /proc/mbt/mbtcharx/status + This command is used to get driver status. + +4) cat /proc/mbt/mbtcharx/config + This command is used to get the current driver settings. + +5) proc commands to config bluetooth parameters + +mbt_drvdbg=[n] + This command is used to set the bit mask of driver debug message control. + + bit 0: MSG PRINTM(MSG,...) + bit 1: FATAL PRINTM(FATAL,...) + bit 2: ERROR PRINTM(ERROR,...) + bit 3: DATA PRINTM(DATA,...) + bit 4: CMD PRINTM(CMD,...) + bit 5: EVENT PRINTM(EVENT,...) + bit 6: INTR PRINTM(INTR,...) + ... + bit 16: DAT_D PRINTM(DAT_D,...), DBG_HEXDUMP(DAT_D,...) + bit 17: CMD_D PRINTM(CMD_D,...), DBG_HEXDUMP(CMD_D,...) + ... + bit 28: ENTRY PRINTM(ENTRY,...), ENTER(), LEAVE() + bit 29: WARN PRINTM(WARN,...) + bit 30: INFO PRINTM(INFO,...) + + Usage: + echo "drvdbg=0x7" > /proc/mbt/mbtcharx/config #enable MSG,FATAL,ERROR messages + +gpio_gap=[n] + This command is used to configure the host sleep parameters. + + bit 8:0 -- Gap + bit 16:8 -- GPIO + where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid + GPIO pin# (e.g. 0-7) or 0xff (Interface, e.g. SDIO will be used instead). + where Gap is the gap in milliseconds between wakeup signal and wakeup event + or 0xff for special setting when GPIO is used to wakeup host. + + Usage: + echo "gpio_gap=0xff80" > /proc/mbt/mbtcharx/config # use Interface (e.g. SDIO) + echo "hscfgcmd=1" > /proc/mbt/mbtcharx/config # gap = 0x80 + + echo "gpio_gap=0x03ff" > /proc/mbt/mbtcharx/config # use gpio 3 + echo "hscfgcmd=1" > /proc/mbt/mbtcharx/config # and special host sleep mode + +psmode=[n] + This command is used to enable/disable auto sleep mode + + where the option is: + 1 -- Enable auto sleep mode + 0 -- Disable auto sleep mode + + Usage: + echo "psmode=1" > /proc/mbt/mbtcharx/config #enable power save mode + echo "idle_timeout=0x0100" > /proc/mbt/mbtcharx/config #configure idle, timeout value in ms + echo "pscmd=1" > /proc/mbt/mbtcharx/config + + echo "psmode=0" > /proc/mbt/mbtcharx/config #disable power save mode + echo "pscmd=1" > /proc/mbt/mbtcharx/config + +sdio_pull_cfg=[n] + This command is used to configure the delay values for pull up and pull down the SDIO lines. + + where value is: + bit 15:0 -- Pull up delay in microsecond + bit 31:16 -- Pull down delay in microsecond + 0xffff disables PullUp and PullDown in BT controller + + Usage: + echo "sdio_pull_cfg=0x00020002" > /proc/mbt/mbtcharx/config # Enable sdio pull control + echo "sdio_pull_ctrl=1" > /proc/mbt/mbtcharx/config # configure sdio pull up delay to 2 microseconds + # configure sdio pull down delay to 2 microseconds + + echo "sdio_pull_cfg=0xffffffff" > /proc/mbt/mbtcharx/config # Disable sdio pull control + echo "sdio_pull_ctrl=1" > /proc/mbt/mbtcharx/config + +6) cat /proc/mbt/mbtcharx/debug + This command is used to get driver debug parameters. + +7) proc command to config debug parameters + +sdcmd52rw=<func> <reg> [data] + This command is used to read/write a controller register in + Secure Digital I/O Interfaces. + + func: The function number to use (0-7) + reg: The address of the register + data: The value to write, read if the value is absent + + For SDIO MMC driver, only function 0 and BT function (2/3) access is allowed. + And there is a limitation for function 0 write, only vendor specific CCCR + registers (0xf0 -0xff) are permiited. + + Usage: + echo "sdcmd52rw= 2 3 0xf" > /proc/mbt/mbtcharx/debug # write 0xf to func 2 address 3 + echo "sdcmd52rw= 0 4" > /proc/mbt/mbtcharx/debug # read func 0 address 4 + +Issue debug_dump command through proc. + Usage: + echo "debug_dump" > /proc/mbt/mbtcharx/debug # dump driver internal debug status. + Use dmesg or cat /var/log/debug to check driver debug messages. + +proc command to enable BT test mode + Usage: + echo "test_mode=1" > /proc/mbt/mbtcharx/config #enable BT test mode + +FOR FW RELOAD + a) Enable parallel firmware download in driver parameter + insmod bt8xxx.ko fw_serail=0 + + b) default fw name for parallel firmware download + sd8887_bt_a2.bin + + c) Trigger FW reload + echo "fw_reload=1" > /proc/mbt/hcix/debug # start inband reset and fw reload. + echo "fw_reload=2" > proc/mbt/hcix/debug # start fw reload + + (Note: This feature should works on SD8977/SD8997 board, + For CAC-A2 board, only works on the board which supports parallel fw download) + + +============================================================================== + U S E R M A N U A L F O R F M A P P + +1) FOR TOOL BUILD + + a) Enter directory + b) make + c) After building, the executable binary "fmapp" is in the directory + +2) FOR TOOL RUN + + a) chmod 777 fmapp (optional) + b) Run fmapp utility based on usage below + Usage: fmapp <Options> devicename ogf ocf [command content] + devicename example: mfmchar0 /mnfccahr0 + FM ogf/ocf example: 0x3f 0x280 + NFC ogf/ocf example: 0x3f 0x281 + Options: + -h: Display help + -v: Verbose + +3) TEST EXAMPLES + ./fmapp mfmchar0 0x3f 0x280 0x01 0x01 + ./fmapp mnfcchar0 0x3f 0x281 0x20 0x00 0x01 0x01
diff --git a/bt_sd8897/bt/bt_drv.h b/bt_sd8897/bt/bt_drv.h new file mode 100644 index 0000000..69b801f --- /dev/null +++ b/bt_sd8897/bt/bt_drv.h
@@ -0,0 +1,901 @@ +/** @file bt_drv.h + * @brief This header file contains global constant/enum definitions, + * global variable declaration. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_DRV_H_ +#define _BT_DRV_H_ + +#include <linux/version.h> +#include <linux/kthread.h> +#include <linux/skbuff.h> +#include <linux/vmalloc.h> + +#include "hci_wrapper.h" + +/** MAX adapter BT driver supported */ +#define MAX_BT_ADAPTER 3 + +#ifndef BIT +/** BIT definition */ +#define BIT(x) (1UL << (x)) +#endif + +#ifdef MBT_64BIT +typedef u64 t_ptr; +#else +typedef u32 t_ptr; +#endif + +/** max number of adapter supported */ +#define MAX_BT_ADAPTER 3 +/** Define drv_mode bit */ +#define DRV_MODE_BT BIT(0) +#define DRV_MODE_NFC BIT(2) + +/** Define devFeature bit */ +#define DEV_FEATURE_BT BIT(0) +#define DEV_FEATURE_BTAMP BIT(1) +#define DEV_FEATURE_BLE BIT(2) +#define DEV_FEATURE_NFC BIT(4) + +/** Define maximum number of radio func supported */ +#define MAX_RADIO_FUNC 4 + +/** MAC address print format */ +#ifndef MACSTR +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +/** MAC address print arguments */ +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Debug level : Message */ +#define DBG_MSG BIT(0) +/** Debug level : Fatal */ +#define DBG_FATAL BIT(1) +/** Debug level : Error */ +#define DBG_ERROR BIT(2) +/** Debug level : Data */ +#define DBG_DATA BIT(3) +/** Debug level : Command */ +#define DBG_CMD BIT(4) +/** Debug level : Event */ +#define DBG_EVENT BIT(5) +/** Debug level : Interrupt */ +#define DBG_INTR BIT(6) + +/** Debug entry : Data dump */ +#define DBG_DAT_D BIT(16) +/** Debug entry : Data dump */ +#define DBG_CMD_D BIT(17) + +/** Debug level : Entry */ +#define DBG_ENTRY BIT(28) +/** Debug level : Warning */ +#define DBG_WARN BIT(29) +/** Debug level : Informative */ +#define DBG_INFO BIT(30) + +#ifdef DEBUG_LEVEL1 +extern u32 mbt_drvdbg; + +#ifdef DEBUG_LEVEL2 +/** Print informative message */ +#define PRINTM_INFO(msg...) \ + do {if (mbt_drvdbg & DBG_INFO) \ + printk(KERN_DEBUG msg); } while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) \ + do {if (mbt_drvdbg & DBG_WARN) \ + printk(KERN_DEBUG msg); } while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) \ + do {if (mbt_drvdbg & DBG_ENTRY) \ + printk(KERN_DEBUG msg); } while (0) +#else +/** Print informative message */ +#define PRINTM_INFO(msg...) do {} while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) do {} while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +/** Print interrupt message */ +#define PRINTM_INTR(msg...) \ + do {if (mbt_drvdbg & DBG_INTR) \ + printk(KERN_DEBUG msg); } while (0) +/** Print event message */ +#define PRINTM_EVENT(msg...) \ + do {if (mbt_drvdbg & DBG_EVENT) \ + printk(KERN_DEBUG msg); } while (0) +/** Print command message */ +#define PRINTM_CMD(msg...) \ + do {if (mbt_drvdbg & DBG_CMD) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data message */ +#define PRINTM_DATA(msg...) \ + do {if (mbt_drvdbg & DBG_DATA) \ + printk(KERN_DEBUG msg); } while (0) +/** Print error message */ +#define PRINTM_ERROR(msg...) \ + do {if (mbt_drvdbg & DBG_ERROR) \ + printk(KERN_ERR msg); } while (0) +/** Print fatal message */ +#define PRINTM_FATAL(msg...) \ + do {if (mbt_drvdbg & DBG_FATAL) \ + printk(KERN_ERR msg); } while (0) +/** Print message */ +#define PRINTM_MSG(msg...) \ + do {if (mbt_drvdbg & DBG_MSG) \ + printk(KERN_ALERT msg); } while (0) + +/** Print data dump message */ +#define PRINTM_DAT_D(msg...) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data dump message */ +#define PRINTM_CMD_D(msg...) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + printk(KERN_DEBUG msg); } while (0) + +/** Print message with required level */ +#define PRINTM(level, msg...) PRINTM_##level(msg) + +/** Debug dump buffer length */ +#define DBG_DUMP_BUF_LEN 64 +/** Maximum number of dump per line */ +#define MAX_DUMP_PER_LINE 16 +/** Maximum data dump length */ +#define MAX_DATA_DUMP_LEN 48 + +/** + * @brief Prints buffer data upto provided length + * + * @param prompt Char pointer + * @param buf Buffer + * @param len Length + * + * @return N/A + */ +static inline void +hexdump(char *prompt, u8 *buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + printk(KERN_DEBUG "%s: len=%d\n", prompt, len); + for (i = 1; i <= len; i++) { + ptr += snprintf(ptr, 4, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +/** Debug hexdump of debug data */ +#define DBG_HEXDUMP_DAT_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + hexdump(x, y, z); } while (0) +/** Debug hexdump of debug command */ +#define DBG_HEXDUMP_CMD_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + hexdump(x, y, z); } while (0) + +/** Debug hexdump */ +#define DBG_HEXDUMP(level, x, y, z) DBG_HEXDUMP_##level(x, y, z) + +/** Mark entry point */ +#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +/** Mark exit point */ +#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +#else +/** Do nothing */ +#define PRINTM(level, msg...) do {} while (0) +/** Do nothing */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) +/** Do nothing */ +#define ENTER() do {} while (0) +/** Do nothing */ +#define LEAVE() do {} while (0) +#endif /* DEBUG_LEVEL1 */ + +/** Bluetooth upload size */ +#define BT_UPLD_SIZE 2312 +/** Bluetooth status success */ +#define BT_STATUS_SUCCESS (0) +/** Bluetooth status pending */ +#define BT_STATUS_PENDING (1) +/** Bluetooth status failure */ +#define BT_STATUS_FAILURE (-1) + +#ifndef TRUE +/** True value */ +#define TRUE 1 +#endif +#ifndef FALSE +/** False value */ +#define FALSE 0 +#endif + +/** Set thread state */ +#define OS_SET_THREAD_STATE(x) set_current_state(x) +/** Time to wait until Host Sleep state change in millisecond */ +#define WAIT_UNTIL_HS_STATE_CHANGED 2000 +/** Time to wait cmd resp in millisecond */ +#define WAIT_UNTIL_CMD_RESP 5000 + +/** Sleep until a condition gets true or a timeout elapses */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000)) +#else +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) +#endif + +/** bt thread structure */ +typedef struct { + /** Task */ + struct task_struct *task; + /** Queue */ + wait_queue_head_t waitQ; + /** PID */ + pid_t pid; + /** Private structure */ + void *priv; +} bt_thread; + +/** + * @brief Activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_activate_thread(bt_thread *thr) +{ + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitQ); + + /** Record the thread pid */ + thr->pid = current->pid; +} + +/** + * @brief De-activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_deactivate_thread(bt_thread *thr) +{ + thr->pid = 0; + return; +} + +/** + * @brief Creates bt thread + * + * @param btfunc Function pointer + * @param thr A pointer to bt_thread structure + * @param name Char pointer + * + * @return N/A + */ +static inline void +bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name) +{ + thr->task = kthread_run(btfunc, thr, "%s", name); +} + +/** + * @brief Delete bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline int +bt_terminate_thread(bt_thread *thr) +{ + /* Check if the thread is active or not */ + if (!thr->pid) + return -1; + + kthread_stop(thr->task); + return 0; +} + +/** + * @brief Set scheduled timeout + * + * @param millisec Time unit in ms + * + * @return N/A + */ +static inline void +os_sched_timeout(u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + + schedule_timeout((millisec * HZ) / 1000); +} + +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__((packed)) +#endif + +/** Data structure for the Marvell Bluetooth device */ +typedef struct _bt_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** m_dev structure */ + struct m_dev m_dev[MAX_RADIO_FUNC]; + + /** Tx download ready flag */ + u8 tx_dnld_rdy; + /** Function */ + u8 fn; + /** Rx unit */ + u8 rx_unit; + /** Power Save mode : Timeout configuration */ + u16 idle_timeout; + /** Power Save mode */ + u8 psmode; + /** Power Save command */ + u8 pscmd; + /** Host Sleep mode */ + u8 hsmode; + /** Host Sleep command */ + u8 hscmd; + /** Low byte is gap, high byte is GPIO */ + u16 gpio_gap; + /** Host Sleep configuration command */ + u8 hscfgcmd; + /** Host Send Cmd Flag */ + u8 sendcmdflag; + /** opcode for Send Cmd */ + u16 send_cmd_opcode; + /** Device Type */ + u8 devType; + /** Device Features */ + u8 devFeature; + /** cmd52 function */ + u8 cmd52_func; + /** cmd52 register */ + u8 cmd52_reg; + /** cmd52 value */ + u8 cmd52_val; + /** SDIO pull control command */ + u8 sdio_pull_ctrl; + /** Low 2 bytes is pullUp, high 2 bytes for pull-down */ + u32 sdio_pull_cfg; + /** Test mode command */ + u8 test_mode; +} bt_dev_t, *pbt_dev_t; + +/** Marvell bt adapter structure */ +typedef struct _bt_adapter { + /** Chip revision ID */ + u8 chip_rev; + /** magic val */ + u8 magic_val; + /** Surprise removed flag */ + u8 SurpriseRemoved; + /** IRQ number */ + int irq; + /** Interrupt counter */ + u32 IntCounter; + /** Tx packet queue */ + struct sk_buff_head tx_queue; + + /** Pointer of fw dump file name */ + char *fwdump_fname; + /** Fw dump queue */ + struct sk_buff_head fwdump_queue; + /** Pending Tx packet queue */ + struct sk_buff_head pending_queue; + /** tx lock flag */ + u8 tx_lock; + /** Power Save mode */ + u8 psmode; + /** Power Save state */ + u8 ps_state; + /** Host Sleep state */ + u8 hs_state; + /** hs skip count */ + u32 hs_skip; + /** suspend_fail flag */ + u8 suspend_fail; + /** suspended flag */ + u8 is_suspended; + /** Number of wakeup tries */ + u8 WakeupTries; + /** Host Sleep wait queue */ + wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__; + /** Host Cmd complet state */ + u8 cmd_complete; + /** last irq recv */ + u8 irq_recv; + /** last irq processed */ + u8 irq_done; + /** sdio int status */ + u8 sd_ireg; + /** buf allocated for transmit */ + u8 *tx_buffer; + /** buf for transmit */ + u8 *tx_buf; + /** buf allocated for read interrupt status */ + u8 *hw_regs_buf; + /** buf for read interrupt status */ + u8 *hw_regs; + /** tx pending */ + u32 skb_pending; +/** Version string buffer length */ +#define MAX_VER_STR_LEN 128 + /** Driver version */ + u8 drv_ver[MAX_VER_STR_LEN]; + /** Number of command timeout */ + u32 num_cmd_timeout; +} bt_adapter, *pbt_adapter; + +/** Length of prov name */ +#define PROC_NAME_LEN 32 + +/** Item data structure */ +struct item_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** Size */ + u32 size; + /** Address */ + t_ptr addr; + /** Offset */ + u32 offset; + /** Flag */ + u32 flag; +}; + +/** Proc private data structure */ +struct proc_private_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** File flag */ + u32 fileflag; + /** Buffer size */ + u32 bufsize; + /** Number of items */ + u32 num_items; + /** Item data */ + struct item_data *pdata; + /** Private structure */ + struct _bt_private *pbt; + /** File operations */ + const struct file_operations *fops; +}; + +/** Device proc structure */ +struct device_proc { + /** Proc directory entry */ + struct proc_dir_entry *proc_entry; + /** num of proc files */ + u8 num_proc_files; + /** pointer to proc_private_data */ + struct proc_private_data *pfiles; +}; + +/** Private structure for the MV device */ +typedef struct _bt_private { + /** Bluetooth device */ + bt_dev_t bt_dev; + /** Adapter */ + bt_adapter *adapter; + /** Firmware helper */ + const struct firmware *fw_helper; + /** Firmware */ + const struct firmware *firmware; + /** Init user configure file */ + const struct firmware *init_user_cfg; + /** Init user configure wait queue token */ + u16 init_user_conf_wait_flag; + /** Init user configure file wait queue */ + wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__; + /** Firmware request start time */ + struct timeval req_fw_time; + /** Hotplug device */ + struct device *hotplug_device; + /** thread to service interrupts */ + bt_thread MainThread; + /** proc data */ + struct device_proc dev_proc[MAX_RADIO_FUNC]; + /** Driver lock */ + spinlock_t driver_lock; + /** Driver lock flags */ + ulong driver_flags; + /** Driver reference flags */ + struct kobject kobj; + /** CRC check flag */ + u16 fw_crc_check; + /** Card type */ + u16 card_type; + /** sdio device */ + const struct sdio_device *psdio_device; + int debug_device_pending; + int debug_ocf_ogf[2]; + u8 fw_reload; +#ifdef BLE_WAKEUP + u8 ble_wakeup_buf_size; + u8 *ble_wakeup_buf; +#endif +} bt_private, *pbt_private; + +/** Disable interrupt */ +#define OS_INT_DISABLE spin_lock_irqsave(&priv->driver_lock, \ + priv->driver_flags) +/** Enable interrupt */ +#define OS_INT_RESTORE spin_unlock_irqrestore(&priv->driver_lock, \ + priv->driver_flags) + +#ifndef HCI_BT_AMP +/** BT_AMP flag for device type */ +#define HCI_BT_AMP 0x80 +#endif + +/** Device type of BT */ +#define DEV_TYPE_BT 0x00 +/** Device type of AMP */ +#define DEV_TYPE_AMP 0x01 +/** Device type of NFC */ +#define DEV_TYPE_NFC 0x04 + +/** Marvell vendor packet */ +#define MRVL_VENDOR_PKT 0xFE + +/** Bluetooth command : Get FW Version */ +#define BT_CMD_GET_FW_VERSION 0x0F +/** Bluetooth command : Sleep mode */ +#define BT_CMD_AUTO_SLEEP_MODE 0x23 +/** Bluetooth command : Host Sleep configuration */ +#define BT_CMD_HOST_SLEEP_CONFIG 0x59 +/** Bluetooth command : Host Sleep enable */ +#define BT_CMD_HOST_SLEEP_ENABLE 0x5A +/** Bluetooth command : Module Configuration request */ +#define BT_CMD_MODULE_CFG_REQ 0x5B + +/** Bluetooth command : PMIC Configure */ +#define BT_CMD_PMIC_CONFIGURE 0x7D + +/** Bluetooth command : SDIO pull up down configuration request */ +#define BT_CMD_SDIO_PULL_CFG_REQ 0x69 +/** Bluetooth command : Set Evt Filter Command */ +#define BT_CMD_SET_EVT_FILTER 0x05 +/** Bluetooth command : Enable Write Scan Command */ +#define BT_CMD_ENABLE_WRITE_SCAN 0x1A +/** Bluetooth command : Enable Device under test mode */ +#define BT_CMD_ENABLE_DEVICE_TESTMODE 0x03 +/** Sub Command: Module Bring Up Request */ +#define MODULE_BRINGUP_REQ 0xF1 +/** Sub Command: Module Shut Down Request */ +#define MODULE_SHUTDOWN_REQ 0xF2 +/** Module already up */ +#define MODULE_CFG_RESP_ALREADY_UP 0x0c +/** Sub Command: Host Interface Control Request */ +#define MODULE_INTERFACE_CTRL_REQ 0xF5 + +/** Bluetooth event : Power State */ +#define BT_EVENT_POWER_STATE 0x20 + +/** Bluetooth Power State : Enable */ +#define BT_PS_ENABLE 0x02 +/** Bluetooth Power State : Disable */ +#define BT_PS_DISABLE 0x03 +/** Bluetooth Power State : Sleep */ +#define BT_PS_SLEEP 0x01 +/** Bluetooth Power State : Awake */ +#define BT_PS_AWAKE 0x02 + +/** Vendor OGF */ +#define VENDOR_OGF 0x3F +/** OGF for reset */ +#define RESET_OGF 0x03 +/** Bluetooth command : Reset */ +#define BT_CMD_RESET 0x03 + +/** Host Sleep activated */ +#define HS_ACTIVATED 0x01 +/** Host Sleep deactivated */ +#define HS_DEACTIVATED 0x00 + +/** Power Save sleep */ +#define PS_SLEEP 0x01 +/** Power Save awake */ +#define PS_AWAKE 0x00 + +/** bt header length */ +#define BT_HEADER_LEN 4 + +#ifndef MAX +/** Return maximum of two */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/** This is for firmware specific length */ +#define EXTRA_LEN 36 + +/** Command buffer size for Marvell driver */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Bluetooth Rx packet buffer size for Marvell driver */ +#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \ + (HCI_MAX_FRAME_SIZE + EXTRA_LEN) + +/** Buffer size to allocate */ +#define ALLOC_BUF_SIZE (((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \ + MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \ + + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) + +/** Request FW timeout in second */ +#define REQUEST_FW_TIMEOUT 30 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** The number of times to try when waiting for downloaded firmware to + become active when multiple interface is present */ +#define MAX_MULTI_INTERFACE_POLL_TRIES 150 + +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** default idle time */ +#define DEFAULT_IDLE_TIME 1000 + +#define BT_CMD_HEADER_SIZE 3 + +/** BT command structure */ +typedef struct _BT_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** Data */ + u8 data[128]; +} __ATTRIB_PACK__ BT_CMD; + +/** BT event structure */ +typedef struct _BT_EVENT { + /** Event Counter */ + u8 EC; + /** Length */ + u8 length; + /** Data */ + u8 data[8]; +} BT_EVENT; + +#if defined(SDIO_SUSPEND_RESUME) +#define DEF_GPIO_GAP 0xffff +#endif + +#ifdef BLE_WAKEUP +#define BD_ADDR_SIZE 6 +/** Vendor specific event */ +#define VENDOR_SPECIFIC_EVENT 0xff +/** system suspend event */ +#define HCI_SYSTEM_SUSPEND_EVT 0x80 +/** system suspend */ +#define HCI_SYSTEM_SUSPEND 0x00 +/** system resume */ +#define HCI_SYSTEM_RESUME 0x01 +/** This function enables ble wake up pattern */ +int bt_config_ble_wakeup(bt_private *priv); +int bt_send_system_event(bt_private *priv, u8 flag); +#endif + +/** This function verify the received event pkt */ +int check_evtpkt(bt_private *priv, struct sk_buff *skb); + +/* Prototype of global function */ +/** This function gets the priv reference */ +struct kobject *bt_priv_get(bt_private *priv); +/** This function release the priv reference */ +void bt_priv_put(bt_private *priv); +/** This function adds the card */ +bt_private *bt_add_card(void *card); +/** This function removes the card */ +int bt_remove_card(void *card); +/** This function handles the interrupt */ +void bt_interrupt(struct m_dev *m_dev); + +/** This function creates proc interface directory structure */ +int bt_root_proc_init(void); +/** This function removes proc interface directory structure */ +int bt_root_proc_remove(void); +/** This function initializes proc entry */ +int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq); +/** This function removes proc interface */ +void bt_proc_remove(bt_private *priv); + +/** This function process the received event */ +int bt_process_event(bt_private *priv, struct sk_buff *skb); +/** This function enables host sleep */ +int bt_enable_hs(bt_private *priv); +/** This function used to send command to firmware */ +int bt_prepare_command(bt_private *priv); +/** This function frees the structure of adapter */ +void bt_free_adapter(bt_private *priv); +/** This function handle the receive packet */ +void bt_recv_frame(bt_private *priv, struct sk_buff *skb); + +/** clean up m_devs */ +void clean_up_m_devs(bt_private *priv); +/** bt driver call this function to register to bus driver */ +int *sbi_register(void); +/** bt driver call this function to unregister to bus driver */ +void sbi_unregister(void); +/** bt driver calls this function to register the device */ +int sbi_register_dev(bt_private *priv); +/** bt driver calls this function to unregister the device */ +int sbi_unregister_dev(bt_private *priv); +/** This function initializes firmware */ +int sbi_download_fw(bt_private *priv); +/** Configures hardware to quit deep sleep state */ +int sbi_wakeup_firmware(bt_private *priv); +/** Module configuration and register device */ +int sbi_register_conf_dpc(bt_private *priv); + +/** This function is used to send the data/cmd to hardware */ +int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb); +/** This function reads the current interrupt status register */ +int sbi_get_int_status(bt_private *priv); +/** This function enables the host interrupts */ +int sbi_enable_host_int(bt_private *priv); +/** This function disables the host interrupts */ +int sbi_disable_host_int(bt_private *priv); + +/** bt fw reload flag */ +extern int bt_fw_reload; +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 +/** This function reload firmware */ +void bt_request_fw_reload(bt_private *priv, int mode); +#define MAX_TX_BUF_SIZE 2312 +/** This function downloads firmware image to the card */ +int sd_download_firmware_w_helper(bt_private *priv); +void bt_dump_sdio_regs(bt_private *priv); +/* dumps the firmware to /var/ or /data/ */ +void bt_dump_firmware_info_v2(bt_private *priv); + +/** Max line length allowed in init config file */ +#define MAX_LINE_LEN 256 +/** Max MAC address string length allowed */ +#define MAX_MAC_ADDR_LEN 18 +/** Max register type/offset/value etc. parameter length allowed */ +#define MAX_PARAM_LEN 12 + +/** Bluetooth command : Mac address configuration */ +#define BT_CMD_CONFIG_MAC_ADDR 0x22 +/** Bluetooth command : Write CSU register */ +#define BT_CMD_CSU_WRITE_REG 0x66 +/** Bluetooth command : Load calibrate data */ +#define BT_CMD_LOAD_CONFIG_DATA 0x61 +/** Bluetooth command : Load calibrate ext data */ +#define BT_CMD_LOAD_CONFIG_DATA_EXT 0x60 + +/** Bluetooth command : BLE deepsleep */ +#define BT_CMD_BLE_DEEP_SLEEP 0x8b + +/** BT_BLE command structure */ +typedef struct _BT_BLE_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** deepsleep flag */ + u8 deepsleep; +} __ATTRIB_PACK__ BT_BLE_CMD; + +/** BT_CSU command structure */ +typedef struct _BT_CSU_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** reg type */ + u8 type; + /** address */ + u8 offset[4]; + /** Data */ + u8 value[2]; +} __ATTRIB_PACK__ BT_CSU_CMD; + +/** This function sets mac address */ +int bt_set_mac_address(bt_private *priv, u8 *mac); +/** This function writes value to CSU registers */ +int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value); +/** BT set user defined init data and param */ +int bt_init_config(bt_private *priv, char *cfg_file); +/** BT PMIC Configure command */ +int bt_pmic_configure(bt_private *priv); +/** This function load the calibrate data */ +int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac); +/** This function load the calibrate ext data */ +int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len); +/** BT set user defined calibration data */ +int bt_cal_config(bt_private *priv, char *cfg_file, char *mac); +/** BT set user defined calibration ext data */ +int bt_cal_config_ext(bt_private *priv, char *cfg_file); +int bt_init_mac_address(bt_private *priv, char *mac); +int bt_set_gpio_pin(bt_private *priv); +/** Bluetooth command : Set gpio pin */ +#define BT_CMD_SET_GPIO_PIN 0xEC +/** Interrupt Raising Edge**/ +#define INT_RASING_EDGE 0 +/** Interrupt Falling Edge**/ +#define INT_FALLING_EDGE 1 +#define DELAY_50_US 50 + +int bt_set_independent_reset(bt_private *priv); +/** Bluetooth command : Independent reset */ +#define BT_CMD_INDEPENDENT_RESET 0x0D + +/** BT HCI command structure */ +typedef struct _BT_HCI_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** cmd type */ + u8 cmd_type; + /** cmd len */ + u8 cmd_len; + /** Data */ + u8 data[6]; +} __ATTRIB_PACK__ BT_HCI_CMD; + +#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8897/bt/bt_init.c b/bt_sd8897/bt/bt_init.c new file mode 100644 index 0000000..9d18912 --- /dev/null +++ b/bt_sd8897/bt/bt_init.c
@@ -0,0 +1,751 @@ +/** @file bt_init.c + * + * @brief This file contains the init functions for BlueTooth + * driver. + * + * Copyright (C) 2011-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/string.h> +#include <linux/firmware.h> + +#include "bt_drv.h" + +extern int bt_req_fw_nowait; + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) + +#define isdigit(c) (('0' <= (c) && (c) <= '9')) +#define isspace(c) (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9))) +/** + * @brief Returns hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +static int +bt_hexval(char chr) +{ + ENTER(); + + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + LEAVE(); + return 0; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +static char * +bt_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Returns hex value of a given ascii string + * + * @param a String to be converted + * + * @return hex value + */ +static int +bt_atox(const char *a) +{ + int i = 0; + ENTER(); + while (isxdigit(*a)) + i = i * 16 + bt_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Converts mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +static void +bt_mac2u8(u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + memcpy(mac_buff, buf, strlen(buf)); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = bt_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = bt_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +/** + * @brief Returns integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_atoi(int *data, char *a) +{ + int i, val = 0, len; + + ENTER(); + + len = strlen(a); + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = bt_atox(a); + return BT_STATUS_SUCCESS; + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + PRINTM(ERROR, "Invalid char %c in string %s\n", a[i], + a); + return BT_STATUS_FAILURE; + } + } + *data = val; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief parse cal-data + * + * @param src a pointer to cal-data string + * @param len len of cal-data + * @param dst a pointer to return cal-data + * @param dst_size size of dest buffer + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size) +{ + const u8 *ptr; + u8 *dptr; + u32 count = 0; + int ret = BT_STATUS_FAILURE; + + ENTER(); + ptr = src; + dptr = dst; + + while ((ptr - src) < len) { + if (*ptr && isspace(*ptr)) { + ptr++; + continue; + } + + if (isxdigit(*ptr)) { + if ((dptr - dst) >= *dst_size) { + PRINTM(ERROR, "cal_file size too big!!!\n"); + goto done; + } + *dptr++ = bt_atox((const char *)ptr); + ptr += 2; + count++; + } else { + ptr++; + } + } + if (dptr == dst) { + ret = BT_STATUS_FAILURE; + goto done; + } + + *dst_size = count; + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief BT get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return -1 or length of the line + */ +int +parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos) +{ + static s32 pos; + u8 *src, *dest; + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while (pos < size && *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + *dest = '\0'; + /* parse new line */ + pos++; + return strlen((const char *)line_pos); +} + +/** + * @brief BT parse ASCII format data to MAC address + * + * @param priv BT private handle + * @param data Source data + * @param size data length + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_init_cfg(bt_private *priv, u8 *data, u32 size) +{ + u8 *pos; + u8 *intf_s, *intf_e; + u8 s[MAX_LINE_LEN]; /* 1 line data */ + u32 line_len; + char dev_name[MAX_PARAM_LEN]; + u8 buf[MAX_PARAM_LEN]; + u8 bt_addr[MAX_MAC_ADDR_LEN]; + u8 bt_mac[ETH_ALEN]; + int setting = 0; + u8 type = 0; + u16 value = 0; + u32 offset = 0; + int ret = BT_STATUS_FAILURE; + + memset(dev_name, 0, sizeof(dev_name)); + memset(bt_addr, 0, sizeof(bt_addr)); + memset(bt_mac, 0, sizeof(bt_mac)); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Need n't process this line */ + + /* Process MAC addr */ + if (strncmp((char *)pos, "mac_addr", 8) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(dev_name, (const char *)intf_s + 1, + intf_e - intf_s - 1); + dev_name[intf_e - intf_s - 1] = '\0'; + strncpy((char *)bt_addr, + (const char *)intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + /* Convert MAC format */ + bt_mac2u8(bt_mac, (char *)bt_addr); + PRINTM(CMD, + "HCI: %s new BT Address " MACSTR "\n", + dev_name, MAC2STR(bt_mac)); + if (BT_STATUS_SUCCESS != + bt_set_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp((char *)pos, "bt_reg", 6) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s + 1, + 1); + buf[1] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + type = (u8)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg type\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = (u8 *)strchr((const char *)intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, + intf_e - intf_s); + buf[intf_e - intf_s] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + offset = (u32)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg offset\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) { + PRINTM(ERROR, + "BT: Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, sizeof(buf)); + if (0 == bt_atoi(&setting, (char *)buf)) + value = (u16) setting; + else { + PRINTM(ERROR, "BT: Fail to parse reg value\n"); + goto done; + } + + PRINTM(CMD, + "BT: Write reg type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + if (BT_STATUS_SUCCESS != + bt_write_reg(priv, type, offset, value)) { + PRINTM(FATAL, + "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + goto done; + } + } + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to bt_private structure + * + * @return N/A + */ +static void +bt_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + bt_private *priv = (bt_private *)context; + + ENTER(); + + if (!firmware) + PRINTM(ERROR, "BT user init config request firmware failed\n"); + + priv->init_user_cfg = firmware; + priv->init_user_conf_wait_flag = TRUE; + wake_up_interruptible(&priv->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief BT set user defined init data and param + * + * @param priv BT private handle + * @param cfg_file user cofig file + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_config(bt_private *priv, char *cfg_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cfg) + ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + u8 cal_data[32]; + u8 *mac_data = NULL; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + u8 *pcal_data = cal_data; + + memset(bt_mac, 0, sizeof(bt_mac)); + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (mac != NULL) { + /* Convert MAC format */ + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: new BT Address " MACSTR "\n", + MAC2STR(bt_mac)); + mac_data = bt_mac; + } + if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size) +{ + u8 cal_data[128]; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (BT_STATUS_SUCCESS != + bt_load_cal_data_ext(priv, cal_data, cal_data_len)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config(bt_private *priv, char *cal_file, char *mac) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config_ext(bt_private *priv, char *cal_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT init mac address from bt_mac parametre when insmod + * + * @param priv a pointer to bt_private structure + * @param bt_mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_mac_address(bt_private *priv, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + int ret = BT_STATUS_FAILURE; + + ENTER(); + memset(bt_mac, 0, sizeof(bt_mac)); + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac)); + ret = bt_set_mac_address(priv, bt_mac); + if (ret != BT_STATUS_SUCCESS) + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre.\n"); + + LEAVE(); + return ret; +}
diff --git a/bt_sd8897/bt/bt_main.c b/bt_sd8897/bt/bt_main.c new file mode 100644 index 0000000..744bba3 --- /dev/null +++ b/bt_sd8897/bt/bt_main.c
@@ -0,0 +1,3958 @@ +/** @file bt_main.c + * + * @brief This file contains the major functions in BlueTooth + * driver. It includes init, exit, open, close and main + * thread etc.. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/** + * @mainpage M-BT Linux Driver + * + * @section overview_sec Overview + * + * The M-BT is a Linux reference driver for Marvell Bluetooth chipset. + * + * @section copyright_sec Copyright + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + */ +#include <linux/module.h> + +#ifdef CONFIG_OF +#include <linux/of.h> +#endif + +#include <linux/mmc/sdio_func.h> + +#include "bt_drv.h" +#include "mbt_char.h" +#include "bt_sdio.h" + +/** Version */ +#define VERSION "C3X14113" + +/** Driver version */ +static char mbt_driver_version[] = "SD8XXX-%s-" VERSION "-(" "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** SD8787 Card */ +#define CARD_SD8787 "SD8787" +/** SD8777 Card */ +#define CARD_SD8777 "SD8777" +/** SD8887 Card */ +#define CARD_SD8887 "SD8887" +/** SD8897 Card */ +#define CARD_SD8897 "SD8897" +/** SD8797 Card */ +#define CARD_SD8797 "SD8797" +/** SD8977 Card */ +#define CARD_SD8977 "SD8977" +/** SD8997 Card */ +#define CARD_SD8997 "SD8997" +/** SD8987 Card */ +#define CARD_SD8987 "SD8987" + +/** Declare and initialize fw_version */ +static char fw_version[32] = "0.0.0.p0"; + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ + +#define AID_NET_BT_STACK 3008 /* bluetooth stack */ + +/** Define module name */ + +#define MODULE_NAME "bt_fm_nfc" + +/** Declaration of chardev class */ +static struct class *chardev_class; + +/** Interface specific variables */ +static int mbtchar_minor; +static int nfcchar_minor; +static int debugchar_minor; + +/** + * The global variable of a pointer to bt_private + * structure variable + **/ +bt_private *m_priv[MAX_BT_ADAPTER]; + +/** Default Driver mode */ +static int drv_mode = (DRV_MODE_BT | DRV_MODE_NFC); + +/** BT interface name */ +static char *bt_name; +/** NFC interface name */ +static char *nfc_name; +/** BT debug interface name */ +static char *debug_name; + +/** fw reload flag */ +int bt_fw_reload; +/** fw serial download flag */ +int bt_fw_serial = 1; + +/** Firmware flag */ +static int fw = 1; +/** default powermode */ +static int psmode = 1; +/** default BLE deep sleep */ +static int deep_sleep = 1; +/** Default CRC check control */ +static int fw_crc_check = 1; +/** Init config file (MAC address, register etc.) */ +static char *init_cfg; +/** Calibration config file (MAC address, init powe etc.) */ +static char *cal_cfg; +/** Calibration config file EXT */ +static char *cal_cfg_ext; +/** Init MAC address */ +static char *bt_mac; +static int mbt_gpio_pin; +static int btindrst = -1; + +/** Setting mbt_drvdbg value based on DEBUG level */ +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff & ~DBG_EVENT) +#else +#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) +#endif /* DEBUG_LEVEL2 */ +u32 mbt_drvdbg = DEFAULT_DEBUG_MASK; +#endif + +#ifdef CONFIG_OF +static int dts_enable = 1; +#endif + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int mbt_pm_keep_power = 1; +#endif + +static int debug_intf = 1; + +static int btpmic = 0; + +/** Offset of sequence number in event */ +#define OFFSET_SEQNUM 4 + +/** + * @brief handle received packet + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * + * @return N/A + */ +void +bt_recv_frame(bt_private *priv, struct sk_buff *skb) +{ + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + mdev_recv_frame(skb); + mdev_bt->stat.byte_rx += skb->len; + } + return; +} + +/** + * @brief Alloc bt device + * + * @return pointer to structure mbt_dev or NULL + */ +struct mbt_dev * +alloc_mbt_dev(void) +{ + struct mbt_dev *mbt_dev; + ENTER(); + + mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL); + if (!mbt_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return mbt_dev; +} + +/** + * @brief Alloc nfc device + * + * @return pointer to structure nfc_dev or NULL + */ +struct nfc_dev * +alloc_nfc_dev(void) +{ + struct nfc_dev *nfc_dev; + ENTER(); + + nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL); + if (!nfc_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return nfc_dev; +} + +/** + * @brief Alloc debug device + * + * @return pointer to structure debug_level or NULL + */ +struct debug_dev * +alloc_debug_dev(void) +{ + struct debug_dev *debug_dev; + ENTER(); + + debug_dev = kzalloc(sizeof(struct debug_dev), GFP_KERNEL); + if (!debug_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return debug_dev; +} + +/** + * @brief Frees m_dev + * + * @return N/A + */ +void +free_m_dev(struct m_dev *m_dev) +{ + ENTER(); + kfree(m_dev->dev_pointer); + m_dev->dev_pointer = NULL; + LEAVE(); +} + +/** + * @brief clean up m_devs + * + * @return N/A + */ +void +clean_up_m_devs(bt_private *priv) +{ + struct m_dev *m_dev = NULL; + int i; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if (m_dev->spec_type == IANYWHERE_SPEC) { + if ((drv_mode & DRV_MODE_BT) && (mbtchar_minor > 0)) + mbtchar_minor--; + m_dev->close(m_dev); + for (i = 0; i < 3; i++) + kfree_skb(((struct mbt_dev *) + (m_dev->dev_pointer))-> + reassembly[i]); + /** unregister m_dev to char_dev */ + if (chardev_class) + chardev_cleanup_one(m_dev, chardev_class); + free_m_dev(m_dev); + } + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL; + } + if (priv->bt_dev.m_dev[NFC_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[NFC_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if ((drv_mode & DRV_MODE_NFC) && (nfcchar_minor > 0)) + nfcchar_minor--; + m_dev->close(m_dev); + /** unregister m_dev to char_dev */ + if (chardev_class) + chardev_cleanup_one(m_dev, chardev_class); + free_m_dev(m_dev); + priv->bt_dev.m_dev[NFC_SEQ].dev_pointer = NULL; + } + if (priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if ((debug_intf) && (debugchar_minor > 0)) + debugchar_minor--; + /** unregister m_dev to char_dev */ + if (chardev_class) + chardev_cleanup_one(m_dev, chardev_class); + free_m_dev(m_dev); + priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = NULL; + } + LEAVE(); + return; +} + +/** + * @brief This function verify the received event pkt + * + * Event format: + * +--------+--------+--------+--------+--------+ + * | Event | Length | ncmd | Opcode | + * +--------+--------+--------+--------+--------+ + * | 1-byte | 1-byte | 1-byte | 2-byte | + * +--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +check_evtpkt(bt_private *priv, struct sk_buff *skb) +{ + struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data; + struct hci_ev_cmd_complete *ec; + u16 opcode, ocf; + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (!priv->bt_dev.sendcmdflag) { + ret = BT_STATUS_FAILURE; + goto exit; + } + if (hdr->evt == HCI_EV_CMD_COMPLETE) { + ec = (struct hci_ev_cmd_complete *) + (skb->data + HCI_EVENT_HDR_SIZE); + opcode = __le16_to_cpu(ec->opcode); + ocf = hci_opcode_ocf(opcode); + PRINTM(CMD, + "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n", + opcode, ocf, priv->bt_dev.send_cmd_opcode); + if (opcode != priv->bt_dev.send_cmd_opcode) { + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (ocf) { + case BT_CMD_MODULE_CFG_REQ: + case BT_CMD_BLE_DEEP_SLEEP: + case BT_CMD_CONFIG_MAC_ADDR: + case BT_CMD_CSU_WRITE_REG: + case BT_CMD_LOAD_CONFIG_DATA: + case BT_CMD_LOAD_CONFIG_DATA_EXT: + case BT_CMD_AUTO_SLEEP_MODE: + case BT_CMD_HOST_SLEEP_CONFIG: + case BT_CMD_SDIO_PULL_CFG_REQ: + case BT_CMD_SET_EVT_FILTER: + case BT_CMD_ENABLE_WRITE_SCAN: + // case BT_CMD_ENABLE_DEVICE_TESTMODE: + case BT_CMD_RESET: + case BT_CMD_PMIC_CONFIGURE: + case BT_CMD_SET_GPIO_PIN: + case BT_CMD_INDEPENDENT_RESET: + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter->cmd_wait_q); + break; + case BT_CMD_GET_FW_VERSION: + { + u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + snprintf(fw_version, sizeof(fw_version), + "%u.%u.%u.p%u", pos[2], pos[1], pos[0], + pos[3]); + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } + case BT_CMD_HOST_SLEEP_ENABLE: + priv->bt_dev.sendcmdflag = FALSE; + break; + default: + /** Ignore command not defined but send by driver */ + if (opcode == priv->bt_dev.send_cmd_opcode) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } else { + ret = BT_STATUS_FAILURE; + } + break; + } + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** +* @brief This function stores the FW dumps received from events in a file +* +* @param priv A pointer to bt_private structure +* @param skb A pointer to rx skb +* +* @return N/A +*/ +void +bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len) +{ + struct file *pfile_fwdump = NULL; + loff_t pos = 0; + u16 seqnum = 0; + struct timeval t; + u32 sec; + + ENTER(); + + seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM)); + + if (priv->adapter->fwdump_fname && seqnum != 1) { + pfile_fwdump = + filp_open((const char *)priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file.\n"); + LEAVE(); + return; + } + } else { + if (!priv->adapter->fwdump_fname) { + gfp_t flag; + flag = (in_atomic() || + irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + priv->adapter->fwdump_fname = kzalloc(64, flag); + } else + memset(priv->adapter->fwdump_fname, 0, 64); + + do_gettimeofday(&t); + sec = (u32)t.tv_sec; + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/var/log/bt_fwdump_", sec); + pfile_fwdump = + filp_open(priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/data/bt_fwdump_", sec); + pfile_fwdump = + filp_open((const char *)priv->adapter-> + fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + } + } + + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file\n"); + LEAVE(); + return; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_fwdump, buf, len, &pos); +#else + kernel_write(pfile_fwdump, buf, len, &pos); +#endif + filp_close(pfile_fwdump, NULL); + LEAVE(); + return; +} + +/** + * @brief This function process the received event + * + * Event format: + * +--------+--------+--------+--------+-----+ + * | EC | Length | Data | + * +--------+--------+--------+--------+-----+ + * | 1-byte | 1-byte | n-byte | + * +--------+--------+--------+--------+-----+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_event(bt_private *priv, struct sk_buff *skb) +{ + int ret = BT_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + BT_EVENT *pevent; + + ENTER(); + if (!m_dev) { + PRINTM(CMD, "BT: bt_process_event without m_dev\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pevent = (BT_EVENT *)skb->data; + if (pevent->EC != 0xff) { + PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC); + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (pevent->data[0]) { + case BT_CMD_AUTO_SLEEP_MODE: + if (pevent->data[2] == BT_STATUS_SUCCESS) { + if (pevent->data[1] == BT_PS_ENABLE) + priv->adapter->psmode = 1; + else + priv->adapter->psmode = 0; + PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name, + (priv->adapter->psmode) ? "Enable" : "Disable"); + + } else { + PRINTM(CMD, "BT: PS Mode Command Fail %s\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_CONFIG: + if (pevent->data[3] == BT_STATUS_SUCCESS) { + PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n", + m_dev->name, pevent->data[1], pevent->data[2]); + } else { + PRINTM(CMD, "BT: %s: HSCFG Command Fail\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_ENABLE: + if (pevent->data[1] == BT_STATUS_SUCCESS) { + priv->adapter->hs_state = HS_ACTIVATED; + if (priv->adapter->suspend_fail == FALSE) { +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED + bt_is_suspended(priv); +#endif +#endif +#endif + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + if (priv->adapter->psmode) + priv->adapter->ps_state = PS_SLEEP; + PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n", + m_dev->name); + + } else { + PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name); + } + break; + case BT_CMD_MODULE_CFG_REQ: + if ((priv->bt_dev.sendcmdflag == TRUE) && + ((pevent->data[1] == MODULE_BRINGUP_REQ) + || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) { + if (pevent->data[1] == MODULE_BRINGUP_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2] && (pevent->data[2] != + MODULE_CFG_RESP_ALREADY_UP)) + ? "Bring up Fail" : "Bring up success"); + priv->bt_dev.devType = pevent->data[3]; + PRINTM(CMD, "devType:%s\n", + (pevent->data[3] == + DEV_TYPE_AMP) ? "AMP controller" : + "BR/EDR controller"); + priv->bt_dev.devFeature = pevent->data[4]; + PRINTM(CMD, "devFeature: %s, %s, %s" + ", %s" + "\n", + ((pevent-> + data[4] & DEV_FEATURE_BT) ? + "BT Feature" : "No BT Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BTAMP) ? + "BTAMP Feature" : "No BTAMP Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BLE) ? + "BLE Feature" : "No BLE Feature") + , + ((pevent-> + data[4] & DEV_FEATURE_NFC) ? + "NFC Feature" : "No NFC Feature") + ); + } + if (pevent->data[1] == MODULE_SHUTDOWN_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2]) ? "Shut down Fail" + : "Shut down success"); + + } + if (pevent->data[2]) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + } else { + PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n"); + ret = BT_STATUS_FAILURE; + } + break; + case BT_EVENT_POWER_STATE: + if (pevent->data[1] == BT_PS_SLEEP) + priv->adapter->ps_state = PS_SLEEP; + if (priv->adapter->ps_state == PS_SLEEP + && (priv->card_type == CARD_TYPE_SD8887) + ) + os_sched_timeout(5); + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); + + break; + case BT_CMD_SDIO_PULL_CFG_REQ: + if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS) + PRINTM(CMD, "BT: %s: SDIO pull configuration success\n", + m_dev->name); + + else { + PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n", + m_dev->name); + + } + break; + default: + PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0], + m_dev->name); + ret = BT_STATUS_FAILURE; + break; + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** + * @brief This function save the dump info to file + * + * @param dir_name directory name + * @param file_name file_name + * @param buf buffer + * @param buf_len buffer length + * + * @return 0 --success otherwise fail + */ +int +bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len) +{ + int ret = BT_STATUS_SUCCESS; + struct file *pfile = NULL; + u8 name[64]; + loff_t pos; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(ERROR, "Can't save dump info to file\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + sprintf((char *)name, "%s/%s", dir_name, file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + sprintf((char *)name, "%s/%s", "/var", file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(ERROR, "Create Dump file for %s error\n", name); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name); + + pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, (const char __user *)buf, buf_len, &pos); + set_fs(fs); +#else + kernel_write(pfile, (const char __user *)buf, buf_len, &pos); +#endif + filp_close(pfile, NULL); + + PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name); + +done: + LEAVE(); + return ret; +} + +#define DEBUG_HOST_READY 0xEE +#define DEBUG_FW_DONE 0xFF +#define DUMP_MAX_POLL_TRIES 200 + +#define DEBUG_DUMP_CTRL_REG_8897 0xE2 +#define DEBUG_DUMP_START_REG_8897 0xE3 +#define DEBUG_DUMP_END_REG_8897 0xEA +#define DEBUG_DUMP_CTRL_REG_8887 0xA2 +#define DEBUG_DUMP_START_REG_8887 0xA3 +#define DEBUG_DUMP_END_REG_8887 0xAA +#define DEBUG_DUMP_CTRL_REG_8977 0xF0 +#define DEBUG_DUMP_START_REG_8977 0xF1 +#define DEBUG_DUMP_END_REG_8977 0xF8 +#define DEBUG_DUMP_CTRL_REG_8997 0xF0 +#define DEBUG_DUMP_START_REG_8997 0xF1 +#define DEBUG_DUMP_END_REG_8997 0xF8 +#define DEBUG_DUMP_CTRL_REG_8987 0xF0 +#define DEBUG_DUMP_START_REG_8987 0xF1 +#define DEBUG_DUMP_END_REG_8987 0xF8 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_APU_REGS = 3, + DUMP_TYPE_CIU_REGS = 4, + DUMP_TYPE_ICU_REGS = 5, + DUMP_TYPE_MAC_REGS = 6, + DUMP_TYPE_EXTEND_7 = 7, + DUMP_TYPE_EXTEND_8 = 8, + DUMP_TYPE_EXTEND_9 = 9, + DUMP_TYPE_EXTEND_10 = 10, + DUMP_TYPE_EXTEND_11 = 11, + DUMP_TYPE_EXTEND_12 = 12, + DUMP_TYPE_EXTEND_13 = 13, + DUMP_TYPE_EXTEND_LAST = 14 +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 + +/** Memory type mapping structure */ +typedef struct { + /** memory name */ + u8 mem_name[MAX_NAME_LEN]; + /** memory pointer */ + u8 *mem_Ptr; + /** file structure */ + struct file *pfile_mem; + /** donbe flag */ + u8 done_flag; +} memory_type_mapping; + +memory_type_mapping bt_mem_type_mapping_tbl[] = { + {"ITCM", NULL, NULL, 0xF0}, + {"DTCM", NULL, NULL, 0xF1}, + {"SQRAM", NULL, NULL, 0xF2}, + {"APU", NULL, NULL, 0xF3}, + {"CIU", NULL, NULL, 0xF4}, + {"ICU", NULL, NULL, 0xF5}, + {"MAC", NULL, NULL, 0xF6}, + {"EXT7", NULL, NULL, 0xF7}, + {"EXT8", NULL, NULL, 0xF8}, + {"EXT9", NULL, NULL, 0xF9}, + {"EXT10", NULL, NULL, 0xFA}, + {"EXT11", NULL, NULL, 0xFB}, + {"EXT12", NULL, NULL, 0xFC}, + {"EXT13", NULL, NULL, 0xFD}, + {"EXTLAST", NULL, NULL, 0xFE}, +}; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +/** + * @brief This function read/write firmware via cmd52 + * + * @param phandle A pointer to moal_handle + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status +bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag) +{ + int ret = 0; + int tries = 0; + u8 ctrl_data = 0; + u8 dbg_dump_ctrl_reg = 0; + + if (priv->card_type == CARD_TYPE_SD8887) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8887; + else if (priv->card_type == CARD_TYPE_SD8897) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8897; + else if (priv->card_type == CARD_TYPE_SD8977) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8977; + else if (priv->card_type == CARD_TYPE_SD8997) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8997; + else if (priv->card_type == CARD_TYPE_SD8987) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8987; + + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) { + ctrl_data = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)-> + func, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != DEBUG_HOST_READY) { + PRINTM(INFO, + "The ctrl reg was changed, re-try again!\n"); + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev. + card)->func, DEBUG_HOST_READY, + dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } + udelay(100); + } + if (ctrl_data == DEBUG_HOST_READY) { + PRINTM(ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void +bt_dump_firmware_info_v2(bt_private *priv) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr = NULL; + u8 dump_num = 0; + u8 idx = 0; + u8 doneflag = 0; + rdwr_status stat; + u8 i = 0; + u8 read_reg = 0; + u32 memory_size = 0; + u8 path_name[64], file_name[32]; + u8 *end_ptr = NULL; + u8 dbg_dump_start_reg = 0; + u8 dbg_dump_end_reg = 0; + + if (!priv) { + PRINTM(ERROR, "Could not dump firmwware info\n"); + return; + } + + if ((priv->card_type != CARD_TYPE_SD8887) && + (priv->card_type != CARD_TYPE_SD8897) + && (priv->card_type != CARD_TYPE_SD8977) && + (priv->card_type != CARD_TYPE_SD8997) + && (priv->card_type != CARD_TYPE_SD8987)) { + PRINTM(MSG, "card_type %d don't support FW dump\n", + priv->card_type); + return; + } + + memset(path_name, 0, sizeof(path_name)); + strcpy((char *)path_name, "/data"); + PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name); + + if (priv->card_type == CARD_TYPE_SD8887) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8887; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8887; + } else if (priv->card_type == CARD_TYPE_SD8897) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8897; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8897; + } else if (priv->card_type == CARD_TYPE_SD8977) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8977; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8977; + } else if (priv->card_type == CARD_TYPE_SD8997) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8997; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8997; + } else if (priv->card_type == CARD_TYPE_SD8987) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8987; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8987; + } + + sbi_wakeup_firmware(priv); + sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + /* start dump fw memory */ + PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n"); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + reg = dbg_dump_start_reg; + dump_num = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ MEM NUM ERR\n"); + goto done; + } + + /* read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + if (RDWR_STATUS_FAILURE == + bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + memory_size = 0; + reg = dbg_dump_start_reg; + for (i = 0; i < 4; i++) { + read_reg = + sdio_readb(((struct sdio_mmc_card *)priv-> + bt_dev.card)->func, reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + if (memory_size == 0) { + PRINTM(MSG, "Firmware Dump Finished!\n"); + break; + } else { + PRINTM(MSG, "%s_SIZE=0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + memory_size); + bt_mem_type_mapping_tbl[idx].mem_Ptr = + vmalloc(memory_size + 1); + if ((ret != BT_STATUS_SUCCESS) || + !bt_mem_type_mapping_tbl[idx].mem_Ptr) { + PRINTM(ERROR, + "Error: vmalloc %s buffer failed!!!\n", + bt_mem_type_mapping_tbl[idx].mem_name); + goto done; + } + dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr; + end_ptr = dbg_ptr + memory_size; + } + doneflag = bt_mem_type_mapping_tbl[idx].done_flag; + PRINTM(MSG, "Start %s output, please wait...\n", + bt_mem_type_mapping_tbl[idx].mem_name); + do { + stat = bt_cmd52_rdwr_firmware(priv, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = + sdio_readb(((struct sdio_mmc_card *) + priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + PRINTM(MSG, + "pre-allocced buf is not enough\n"); + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MSG, "%s done:" + "size = 0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + (unsigned int)(dbg_ptr - + bt_mem_type_mapping_tbl + [idx].mem_Ptr)); + memset(file_name, 0, sizeof(file_name)); + sprintf((char *)file_name, "%s%s", "file_bt_", + bt_mem_type_mapping_tbl[idx].mem_name); + if (BT_STATUS_SUCCESS != + bt_save_dump_info_to_file((char *)path_name, + (char *)file_name, + bt_mem_type_mapping_tbl + [idx].mem_Ptr, + memory_size)) + PRINTM(MSG, + "Can't save dump file %s in %s\n", + file_name, path_name); + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + break; + } + } while (1); + } + PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n"); + /* end dump fw memory */ +done: + sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + for (idx = 0; idx < dump_num; idx++) { + if (bt_mem_type_mapping_tbl[idx].mem_Ptr) { + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + } + } + PRINTM(MSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function shows debug info for timeout of command sending. + * + * @param adapter A pointer to bt_private + * @param cmd Timeout command id + * + * @return N/A + */ +static void +bt_cmd_timeout_func(bt_private *priv, u16 cmd) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + + adapter->num_cmd_timeout++; + + PRINTM(ERROR, "Version = %s\n", adapter->drv_ver); + PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd); + PRINTM(ERROR, "Number of command timeout = %d\n", + adapter->num_cmd_timeout); + PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter); + PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode); + PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state); + PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state); + PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip); + PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail); + PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended); + PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries); + PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete); + PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv); + PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done); + PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending); + PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg); + bt_dump_sdio_regs(priv); + LEAVE(); +} + +/** + * @brief This function queue frame + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to sk_buff structure + * + * @return N/A + */ +static void +bt_queue_frame(bt_private *priv, struct sk_buff *skb) +{ + skb_queue_tail(&priv->adapter->tx_queue, skb); +} + +/** + * @brief This function send reset cmd to firmware + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_reset_command(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET); + pcmd->length = 0x00; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, 3); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue Reset Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Reset timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_RESET); + } else { + PRINTM(CMD, "BT: Reset Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends module cfg cmd to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param subcmd sub command + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_module_cfg_cmd(bt_private *priv, int subcmd) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ); + pcmd->length = 1; + pcmd->data[0] = subcmd; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue module cfg Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + /* + On some Android platforms certain delay is needed for HCI daemon to + remove this module and close itself gracefully. Otherwise it hangs. + This 10ms delay is a workaround for such platforms as the root cause + has not been found yet. */ + mdelay(10); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n", + subcmd, priv->bt_dev.sendcmdflag); + bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ); + } else { + PRINTM(CMD, "BT: module cfg Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables power save mode + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_ps(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE); + if (priv->bt_dev.psmode) + pcmd->data[0] = BT_PS_ENABLE; + else + pcmd->data[0] = BT_PS_DISABLE; + if (priv->bt_dev.idle_timeout) { + pcmd->length = 3; + pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff); + pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8; + } else { + pcmd->length = 1; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: psmode timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends hscfg command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_hscfg_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG); + pcmd->length = 2; + pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8; + pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: HSCFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends sdio pull ctrl command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_sdio_pull_ctrl_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ); + pcmd->length = 4; + pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff); + pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8; + pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16; + pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, + "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0], + pcmd->data[3], pcmd->data[2]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends command to configure PMIC + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_pmic_configure(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: PMIC Configure timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables host sleep + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_hs(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + priv->adapter->suspend_fail = FALSE; + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + PRINTM(CMD, "Queue hs enable Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->hs_state, + WAIT_UNTIL_HS_STATE_CHANGED)) { + PRINTM(MSG, "BT: Enable host sleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE); + } + OS_INT_DISABLE; + if ((priv->adapter->hs_state == HS_ACTIVATED) || + (priv->adapter->is_suspended == TRUE)) { + OS_INT_RESTORE; + PRINTM(MSG, "BT: suspend success! skip=%d\n", + priv->adapter->hs_skip); + } else { + priv->adapter->suspend_fail = TRUE; + OS_INT_RESTORE; + priv->adapter->hs_skip++; + ret = BT_STATUS_FAILURE; + PRINTM(MSG, + "BT: suspend skipped! " + "state=%d skip=%d ps_state= %d WakeupTries=%d\n", + priv->adapter->hs_state, priv->adapter->hs_skip, + priv->adapter->ps_state, priv->adapter->WakeupTries); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Set Evt Filter + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_evt_filter(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER); + pcmd->length = 0x03; + pcmd->data[0] = 0x02; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set Evt Filter timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Write Scan - Page and Inquiry + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_write_scan(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN); + pcmd->length = 0x01; + pcmd->data[0] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Write Scan timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Device under test mode + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_device_under_testmode(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE); + pcmd->length = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables test mode and send cmd + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_test_mode(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + /** Set Evt Filter Command */ + ret = bt_set_evt_filter(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n"); + goto exit; + } + + /** Enable Write Scan Command */ + ret = bt_enable_write_scan(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n"); + goto exit; + } + + /** Enable Device under test mode */ + ret = bt_enable_device_under_testmode(priv); + if (ret != BT_STATUS_SUCCESS) + PRINTM(ERROR, + "BT test_mode: Enable device under testmode fail\n"); + +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function set GPIO pin + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_gpio_pin(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + /**Interrupt falling edge **/ + u8 gpio_int_edge = INT_FALLING_EDGE; + /**Delay 50 usec **/ + u8 gpio_pulse_width = DELAY_50_US; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SET_GPIO_PIN); + pcmd->data[0] = mbt_gpio_pin; + pcmd->data[1] = gpio_int_edge; + pcmd->data[2] = gpio_pulse_width; + pcmd->length = 3; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set GPIO pin: timeout!\n"); + bt_cmd_timeout_func(priv, BT_CMD_SET_GPIO_PIN); + } +exit: + LEAVE(); + return ret; +} + +#define DISABLE_RESET 0x0 +#define ENABLE_OUTBAND_RESET 0x1 +#define ENABLE_INBAND_RESET 0x02 +#define DEFAULT_GPIO 0xff +/** + * @brief This function set GPIO pin + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_independent_reset(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + u8 mode, gpio; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET); + mode = btindrst & 0xff; + gpio = (btindrst & 0xff00) >> 8; + if (mode == ENABLE_OUTBAND_RESET) { + pcmd->data[0] = ENABLE_OUTBAND_RESET; + if (!gpio) + pcmd->data[1] = DEFAULT_GPIO; + else + pcmd->data[1] = gpio; + } else if (mode == ENABLE_INBAND_RESET) { + pcmd->data[0] = ENABLE_INBAND_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else if (mode == DISABLE_RESET) { + pcmd->data[0] = DISABLE_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else { + PRINTM(WARN, "Unsupport mode\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio); + pcmd->length = 2; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Independent reset : timeout!\n"); + bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets ble deepsleep mode + * + * @param priv A pointer to bt_private structure + * @param mode TRUE/FALSE + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_ble_deepsleep(bt_private *priv, int mode) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_BLE_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_BLE_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP); + pcmd->length = 1; + pcmd->deepsleep = mode; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_BLE_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode, + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function gets FW version + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_get_fw_version(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION); + pcmd->length = 0x01; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, 4); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Get FW version: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets mac address + * + * @param priv A pointer to bt_private structure + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_mac_address(bt_private *priv, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + int i = 0; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR); + pcmd->length = 8; + pcmd->cmd_type = MRVL_VENDOR_PKT; + pcmd->cmd_len = 6; + for (i = 0; i < 6; i++) + pcmd->data[i] = mac[5 - i]; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_HCI_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac), + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set mac addr: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + int i = 0; + /* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01 + 0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0xF0}; */ + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA); + pcmd->length = 0x20; + pcmd->data[0] = 0x00; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x00; + pcmd->data[3] = 0x1C; + /* swip cal-data byte */ + for (i = 4; i < 32; i++) + pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i)); + if (mac != NULL) { + pcmd->data[2] = 0x01; /* skip checksum */ + for (i = 24; i < 30; i++) + pcmd->data[i] = mac[29 - i]; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate EXT data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT); + pcmd->length = cfg_data_len; + + memcpy(pcmd->data, config_data, cfg_data_len); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function writes value to CSU registers + * + * @param priv A pointer to bt_private structure + * @param type reg type + * @param offset register address + * @param value register value to write + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CSU_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CSU_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG); + pcmd->length = 7; + pcmd->type = type; + pcmd->offset[0] = (offset & 0x000000ff); + pcmd->offset[1] = (offset & 0x0000ff00) >> 8; + pcmd->offset[2] = (offset & 0x00ff0000) >> 16; + pcmd->offset[3] = (offset & 0xff000000) >> 24; + pcmd->value[0] = (value & 0x00ff); + pcmd->value[1] = (value & 0xff00) >> 8; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_CSU_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n", + type, offset, value); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Set CSU reg timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function used to restore tx_queue + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_restore_tx_queue(bt_private *priv) +{ + struct sk_buff *skb = NULL; + while (!skb_queue_empty(&priv->adapter->pending_queue)) { + skb = skb_dequeue(&priv->adapter->pending_queue); + if (skb) + bt_queue_frame(priv, skb); + } + wake_up_interruptible(&priv->MainThread.waitQ); +} + +/** + * @brief This function used to send command to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_prepare_command(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (priv->bt_dev.hscfgcmd) { + priv->bt_dev.hscfgcmd = 0; + ret = bt_send_hscfg_cmd(priv); + } + if (priv->bt_dev.pscmd) { + priv->bt_dev.pscmd = 0; + ret = bt_enable_ps(priv); + } + if (priv->bt_dev.sdio_pull_ctrl) { + priv->bt_dev.sdio_pull_ctrl = 0; + ret = bt_send_sdio_pull_ctrl_cmd(priv); + } + if (priv->bt_dev.hscmd) { + priv->bt_dev.hscmd = 0; + if (priv->bt_dev.hsmode) + ret = bt_enable_hs(priv); + else { + ret = sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + } + } + if (priv->bt_dev.test_mode) { + priv->bt_dev.test_mode = 0; + ret = bt_enable_test_mode(priv); + } + LEAVE(); + return ret; +} + +/** @brief This function processes a single packet + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to skb which includes TX packet + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +send_single_packet(bt_private *priv, struct sk_buff *skb) +{ + int ret; + struct sk_buff *new_skb = NULL; + ENTER(); + if (!skb || !skb->data) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) { + PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, + BT_UPLD_SIZE); + LEAVE(); + return BT_STATUS_FAILURE; + } + if (skb_headroom(skb) < BT_HEADER_LEN) { + new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN); + if (!new_skb) { + PRINTM(ERROR, "TX error: realloc_headroom failed %d\n", + BT_HEADER_LEN); + kfree_skb(skb); + LEAVE(); + return BT_STATUS_FAILURE; + } else { + if (new_skb != skb) + dev_kfree_skb_any(skb); + skb = new_skb; + } + } + /* This is SDIO/PCIE specific header length: byte[3][2][1], * type: + byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) + */ + skb_push(skb, BT_HEADER_LEN); + skb->data[0] = (skb->len & 0x0000ff); + skb->data[1] = (skb->len & 0x00ff00) >> 8; + skb->data[2] = (skb->len & 0xff0000) >> 16; + skb->data[3] = bt_cb(skb)->pkt_type; + if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT) + PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n", + __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len); + ret = sbi_host_to_card(priv, skb->data, skb->len); + if (ret == BT_STATUS_FAILURE) + ((struct m_dev *)skb->dev)->stat.err_tx++; + else + ((struct m_dev *)skb->dev)->stat.byte_tx += skb->len; + if (ret != BT_STATUS_PENDING) + kfree_skb(skb); + LEAVE(); + return ret; +} + +#ifdef CONFIG_OF +/** + * @brief This function read the initial parameter from device tress + * + * + * @return N/A + */ +static void +bt_init_from_dev_tree(void) +{ + struct device_node *dt_node = NULL; + struct property *prop; + u32 data; + const char *string_data; + + ENTER(); + + if (!dts_enable) { + PRINTM(CMD, "DTS is disabled!"); + return; + } + + dt_node = of_find_node_by_name(NULL, "sd8xxx-bt"); + if (!dt_node) { + LEAVE(); + return; + } + for_each_property_of_node(dt_node, prop) { +#ifdef DEBUG_LEVEL1 + if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(CMD, "mbt_drvdbg=0x%x\n", data); + mbt_drvdbg = data; + } + } +#endif + else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + init_cfg = (char *)string_data; + PRINTM(CMD, "init_cfg=%s\n", init_cfg); + } + } else if (!strncmp + (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg_ext = (char *)string_data; + PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext); + } + } else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg = (char *)string_data; + PRINTM(CMD, "cal_cfg=%s\n", cal_cfg); + } + } else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + bt_mac = (char *)string_data; + PRINTM(CMD, "bt_mac=%s\n", bt_mac); + } + } else if (!strncmp + (prop->name, "mbt_gpio_pin", + strlen("mbt_gpio_pin"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + mbt_gpio_pin = data; + PRINTM(CMD, "mbt_gpio_pin=%d\n", mbt_gpio_pin); + } + } else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btindrst = data; + PRINTM(CMD, "btindrst=%d\n", btindrst); + } + } else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btpmic = data; + PRINTM(CMD, "btpmic=%d\n", btpmic); + } + } + } + LEAVE(); + return; +} +#endif + +/** + * @brief This function initializes the adapter structure + * and set default value to the member of adapter. + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +static void +bt_init_adapter(bt_private *priv) +{ + ENTER(); +#ifdef CONFIG_OF + bt_init_from_dev_tree(); +#endif + skb_queue_head_init(&priv->adapter->tx_queue); + skb_queue_head_init(&priv->adapter->fwdump_queue); + + skb_queue_head_init(&priv->adapter->pending_queue); + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + priv->adapter->fwdump_fname = NULL; + init_waitqueue_head(&priv->adapter->cmd_wait_q); + LEAVE(); +} + +/** + * @brief This function initializes firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (fw == 0) { + sbi_enable_host_int(priv); + goto done; + } + sbi_disable_host_int(priv); + if ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + priv->fw_crc_check = fw_crc_check; + if (sbi_download_fw(priv)) { + PRINTM(ERROR, " FW failed to be download!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +#define FW_POLL_TRIES 100 +#define SD8897_FW_RESET_REG 0x0E8 +#define SD8887_FW_RESET_REG 0x0B6 +#define SD8977_SD8997_FW_RESET_REG 0x0EE +#define SD8887_SD8897_FW_RESET_VAL 1 +#define SD8977_SD8997_FW_RESET_VAL 0x99 + +/** + * @brief This function reload firmware + * + * @param priv A pointer to bt_private + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +static int +bt_reload_fw(bt_private *priv, int mode) +{ + int ret = 0, tries = 0; + u8 value = 1; + u32 reset_reg = 0; + u8 reset_val = 0; + + ENTER(); + if ((mode != FW_RELOAD_SDIO_INBAND_RESET) && + (mode != FW_RELOAD_NO_EMULATION)) { + PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode); + return -EFAULT; + } + + /** flush pending tx_queue */ + skb_queue_purge(&priv->adapter->tx_queue); + if (mode == FW_RELOAD_SDIO_INBAND_RESET) { + if (priv->card_type == CARD_TYPE_SD8887) { + reset_reg = SD8887_FW_RESET_REG; + reset_val = SD8887_SD8897_FW_RESET_VAL; + } else if (priv->card_type == CARD_TYPE_SD8897) { + reset_reg = SD8897_FW_RESET_REG; + reset_val = SD8887_SD8897_FW_RESET_VAL; + } else if ((priv->card_type == CARD_TYPE_SD8977) || + (priv->card_type == CARD_TYPE_SD8997) || + (priv->card_type == CARD_TYPE_SD8987)) { + reset_reg = SD8977_SD8997_FW_RESET_REG; + reset_val = SD8977_SD8997_FW_RESET_VAL; + } + sbi_disable_host_int(priv); + /** Wake up firmware firstly */ + sbi_wakeup_firmware(priv); + + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = sd_write_reg(priv, reset_reg, 0xba); + if (!ret) { + ret = sd_read_reg(priv, reset_reg, &value); + if (!ret && (value == 0xba)) { + PRINTM(MSG, "Fw wake up\n"); + break; + } + } + udelay(1000); + } + + ret = sd_write_reg(priv, reset_reg, reset_val); + if (ret) { + PRINTM(ERROR, "Failed to write register.\n"); + goto done; + } + + /** Poll register around 1 ms */ + for (; tries < FW_POLL_TRIES; ++tries) { + ret = sd_read_reg(priv, reset_reg, &value); + if (ret) { + PRINTM(ERROR, "Failed to read register.\n"); + goto done; + } + if (value == 0) + /** FW is ready */ + break; + udelay(1000); + } + if (value) { + PRINTM(ERROR, + "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + } + + sbi_enable_host_int(priv); + /** reload FW */ + ret = bt_init_fw(priv); + if (ret) { + PRINTM(ERROR, "Re download firmware failed.\n"); + ret = -EFAULT; + } + LEAVE(); + return ret; +done: + sbi_enable_host_int(priv); + LEAVE(); + return ret; +} + +/** + * @brief This function request to reload firmware + * + * @param priv A pointer to bt_private + * @param mode fw reload mode. + * + * @return N/A + */ +void +bt_request_fw_reload(bt_private *priv, int mode) +{ + ENTER(); + if (mode == FW_RELOAD_WITH_EMULATION) { + bt_fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MSG, "BT: FW reload with re-emulation...\n"); + LEAVE(); + return; + } + /** Reload FW */ + priv->fw_reload = TRUE; + if (bt_reload_fw(priv, mode)) { + PRINTM(ERROR, "FW reload fail\n"); + goto done; + } + priv->fw_reload = FALSE; + /** Other operation here? */ +done: + LEAVE(); + return; +} + +/** + * @brief This function frees the structure of adapter + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_free_adapter(bt_private *priv) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->fwdump_queue); + kfree(adapter->tx_buffer); + kfree(adapter->hw_regs_buf); + /* Free allocated memory for fwdump filename */ + if (adapter->fwdump_fname) { + kfree(adapter->fwdump_fname); + adapter->fwdump_fname = NULL; + } + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; + + LEAVE(); +} + +/** + * @brief This function handles the wrapper_dev ioctl + * + * @param hev A pointer to wrapper_dev structure + * @cmd ioctl cmd + * @arg argument + * @return -ENOIOCTLCMD + */ +static int +mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg) +{ + bt_private *priv = NULL; + int ret = 0; +#ifdef BLE_WAKEUP + u16 len; +#endif + + ENTER(); + + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n"); + ret = -ENODEV; + goto done; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n", + m_dev->flags); + ret = -EBUSY; + goto done; + } + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { +#ifdef BLE_WAKEUP + case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM: + PRINTM(MSG, "BT: Set ble wakeup parameters\n"); + if (copy_from_user(&len, arg, sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params length\n"); + ret = -EFAULT; + goto done; + } + /** Convert little endian length */ + len = __le16_to_cpu(len); + if (len < 2) { + PRINTM(ERROR, + "BT_IOCTL: Invalid ble wakeup params len %d\n", + len); + ret = -EFAULT; + goto done; + } + if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + priv->ble_wakeup_buf = + kzalloc(len + sizeof(u16), GFP_KERNEL); + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t" + "for ble wakeup parameters\n"); + ret = -ENOMEM; + goto done; + } + priv->ble_wakeup_buf_size = len + sizeof(u16); + } + if (copy_from_user + (priv->ble_wakeup_buf, arg, len + sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf, + len + sizeof(u16)); + break; +#endif + default: + break; + } + +done: +#ifdef BLE_WAKEUP + if (ret && priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + LEAVE(); + return ret; +} + +/** + * @brief This function handles wrapper device destruct + * + * @param m_dev A pointer to m_dev structure + * + * @return N/A + */ +static void +mdev_destruct(struct m_dev *m_dev) +{ + ENTER(); + LEAVE(); + return; +} + +/** + * @brief This function handles the wrapper device transmit + * + * @param m_dev A pointer to m_dev structure + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb) +{ + bt_private *priv = NULL; + + ENTER(); + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n"); + LEAVE(); + return -ENODEV; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n", + m_dev->flags); + LEAVE(); + return -EBUSY; + } + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + m_dev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + m_dev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + m_dev->stat.sco_tx++; + break; + } + + if (m_dev->dev_type == DEBUG_TYPE) { + /* remember the ogf_ocf */ + priv->debug_device_pending = 1; + priv->debug_ocf_ogf[0] = skb->data[0]; + priv->debug_ocf_ogf[1] = skb->data[1]; + PRINTM(CMD, "debug_ocf_ogf[0]=0x%x debug_ocf_ogf[1]=0x%x\n", + priv->debug_ocf_ogf[0], priv->debug_ocf_ogf[1]); + } + + if (priv->adapter->tx_lock == TRUE) + skb_queue_tail(&priv->adapter->pending_queue, skb); + else + bt_queue_frame(priv, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function flushes the transmit queue + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_flush(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->pending_queue); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function closes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_close(struct m_dev *m_dev) +{ + + ENTER(); + mdev_req_lock(m_dev); + if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + + if (m_dev->flush) + m_dev->flush(m_dev); + /* wait up pending read and unregister char dev */ + wake_up_interruptible(&m_dev->req_wait_q); + /* Drop queues */ + skb_queue_purge(&m_dev->rx_q); + if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + module_put(THIS_MODULE); + m_dev->flags = 0; + mdev_req_unlock(m_dev); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function opens the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +static int +mdev_open(struct m_dev *m_dev) +{ + ENTER(); + + if (try_module_get(THIS_MODULE) == 0) + return BT_STATUS_FAILURE; + + set_bit(HCI_RUNNING, &m_dev->flags); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function queries the wrapper device + * + * @param m_dev A pointer to m_dev structure + * @param arg arguement + * + * @return BT_STATUS_SUCCESS or other + */ +void +mdev_query(struct m_dev *m_dev, void *arg) +{ + struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer; + + ENTER(); + if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type))) + PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n"); + + LEAVE(); +} + +/** + * @brief This function initializes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +void +init_m_dev(struct m_dev *m_dev) +{ + m_dev->dev_pointer = NULL; + m_dev->driver_data = NULL; + m_dev->dev_type = 0; + m_dev->spec_type = 0; + skb_queue_head_init(&m_dev->rx_q); + init_waitqueue_head(&m_dev->req_wait_q); + init_waitqueue_head(&m_dev->rx_wait_q); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + init_MUTEX(&m_dev->req_lock); +#else + sema_init(&m_dev->req_lock, 1); +#endif + spin_lock_init(&m_dev->rxlock); + memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats)); + m_dev->open = mdev_open; + m_dev->close = mdev_close; + m_dev->flush = mdev_flush; + m_dev->send = mdev_send_frame; + m_dev->destruct = mdev_destruct; + m_dev->ioctl = mdev_ioctl; + m_dev->query = mdev_query; + m_dev->owner = THIS_MODULE; + +} + +/** + * @brief This function handles the major job in bluetooth driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to bt_thread structure + * @return BT_STATUS_SUCCESS + */ +static int +bt_service_main_thread(void *data) +{ + bt_thread *thread = data; + bt_private *priv = thread->priv; + bt_adapter *adapter = priv->adapter; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + wait_queue_t wait; +#else + wait_queue_entry_t wait; +#endif + struct sk_buff *skb; + ENTER(); + bt_activate_thread(thread); + init_waitqueue_entry(&wait, current); + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&thread->waitQ, &wait); + OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE); + if (priv->adapter->WakeupTries || + ((!priv->adapter->IntCounter) && + (!priv->bt_dev.tx_dnld_rdy || + skb_queue_empty(&priv->adapter->tx_queue)) + && skb_queue_empty(&priv->adapter->fwdump_queue) + )) { + PRINTM(INFO, "Main: Thread sleeping...\n"); + schedule(); + } + OS_SET_THREAD_STATE(TASK_RUNNING); + remove_wait_queue(&thread->waitQ, &wait); + if (kthread_should_stop() || adapter->SurpriseRemoved) { + PRINTM(INFO, "main-thread: break from main thread: " + "SurpriseRemoved=0x%x\n", + adapter->SurpriseRemoved); + break; + } + + PRINTM(INFO, "Main: Thread waking up...\n"); + + if (priv->adapter->IntCounter) { + OS_INT_DISABLE; + adapter->IntCounter = 0; + OS_INT_RESTORE; + sbi_get_int_status(priv); + } else if ((priv->adapter->ps_state == PS_SLEEP) && + !skb_queue_empty(&priv->adapter->tx_queue)) { + priv->adapter->WakeupTries++; + sbi_wakeup_firmware(priv); + continue; + } + if (priv->adapter->ps_state == PS_SLEEP) + continue; + if (priv->bt_dev.tx_dnld_rdy == TRUE) { + if (!skb_queue_empty(&priv->adapter->tx_queue)) { + skb = skb_dequeue(&priv->adapter->tx_queue); + if (skb) + send_single_packet(priv, skb); + } + } + if (!skb_queue_empty(&priv->adapter->fwdump_queue)) { + skb = skb_dequeue(&priv->adapter->fwdump_queue); + if (skb) { + bt_store_firmware_dump(priv, skb->data, + skb->len); + dev_kfree_skb_any(skb); + } + } + } + bt_deactivate_thread(thread); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param m_dev A pointer to m_dev structure + * @return N/A + */ +void +bt_interrupt(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + if (!priv || !priv->adapter) { + LEAVE(); + return; + } + PRINTM(INTR, "*\n"); + priv->adapter->ps_state = PS_AWAKE; + if (priv->adapter->hs_state == HS_ACTIVATED) { + PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name); + priv->adapter->hs_state = HS_DEACTIVATED; + } + priv->adapter->WakeupTries = 0; + priv->adapter->IntCounter++; + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); +} + +/** + * @brief Dynamic release of char dev + * + * @param kobj A pointer to kobject structure + * + * @return N/A + */ +static void +char_dev_release_dynamic(struct kobject *kobj) +{ + struct char_dev *cdev = container_of(kobj, struct char_dev, kobj); + ENTER(); + PRINTM(INFO, "free char_dev\n"); + kfree(cdev); + LEAVE(); +} + +static struct kobj_type ktype_char_dev_dynamic = { + .release = char_dev_release_dynamic, +}; + +/** + * @brief Allocation of char dev + * + * @param N/A + * + * @return char_dev + */ +static struct char_dev * +alloc_char_dev(void) +{ + struct char_dev *cdev; + ENTER(); + cdev = kzalloc(sizeof(struct char_dev), GFP_KERNEL); + if (cdev) { + kobject_init(&cdev->kobj, &ktype_char_dev_dynamic); + PRINTM(INFO, "alloc char_dev\n"); + } + return cdev; +} + +/** + * @brief Dynamic release of bt private + * + * @param kobj A pointer to kobject structure + * + * @return N/A + */ +static void +bt_private_dynamic_release(struct kobject *kobj) +{ + bt_private *priv = container_of(kobj, bt_private, kobj); + ENTER(); + PRINTM(INFO, "free bt priv\n"); + kfree(priv); + LEAVE(); +} + +static struct kobj_type ktype_bt_private_dynamic = { + .release = bt_private_dynamic_release, +}; + +/** + * @brief Allocation of bt private + * + * @param N/A + * + * @return bt_private + */ +static bt_private * +bt_alloc_priv(void) +{ + bt_private *priv; + ENTER(); + priv = kzalloc(sizeof(bt_private), GFP_KERNEL); + if (priv) { + kobject_init(&priv->kobj, &ktype_bt_private_dynamic); + PRINTM(INFO, "alloc bt priv\n"); + } + LEAVE(); + return priv; +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return kobject structure + */ +struct kobject * +bt_priv_get(bt_private *priv) +{ + PRINTM(INFO, "bt priv get object"); + return kobject_get(&priv->kobj); +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return N/A + */ +void +bt_priv_put(bt_private *priv) +{ + PRINTM(INFO, "bt priv put object"); + kobject_put(&priv->kobj); +} + +/** + * @brief This function send init commands to firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_init_cmd(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + if (mbt_gpio_pin) { + ret = bt_set_gpio_pin(priv); + if (ret < 0) { + PRINTM(FATAL, "GPIO pin set failed!\n"); + goto done; + } + } + ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + if (ret < 0) { + PRINTM(FATAL, "Module cfg command send failed!\n"); + goto done; + } + if (btindrst != -1) { + ret = bt_set_independent_reset(priv); + if (ret < 0) { + PRINTM(FATAL, "Independent reset failed!\n"); + goto done; + } + } + if (btpmic + && ((priv->card_type == CARD_TYPE_SD8997) || + (priv->card_type == CARD_TYPE_SD8977)) + ) { + if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) { + PRINTM(FATAL, "BT: PMIC Configure failed \n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE); + if (ret < 0) { + PRINTM(FATAL, "%s BLE deepsleep failed!\n", + deep_sleep ? "Enable" : "Disable"); + goto done; + } + if (psmode) { + priv->bt_dev.psmode = TRUE; + priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME; + ret = bt_enable_ps(priv); + if (ret < 0) { + PRINTM(FATAL, "Enable PS mode failed!\n"); + goto done; + } + } +#if defined(SDIO_SUSPEND_RESUME) + priv->bt_dev.gpio_gap = DEF_GPIO_GAP; + ret = bt_send_hscfg_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "Send HSCFG failed!\n"); + goto done; + } +#endif + priv->bt_dev.sdio_pull_cfg = 0xffffffff; + priv->bt_dev.sdio_pull_ctrl = 0; + wake_up_interruptible(&priv->MainThread.waitQ); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reinit firmware after redownload firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_reinit_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + /* block all the packet from bluez */ + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) + priv->adapter->tx_lock = TRUE; + + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) { + priv->adapter->tx_lock = FALSE; + bt_restore_tx_queue(priv); + } + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); +done: + return ret; +} + +/** + * @brief Module configuration and register device + * + * @param priv A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_conf_dpc(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct mbt_dev *mbt_dev = NULL; + struct nfc_dev *nfc_dev = NULL; + struct debug_dev *debug_dev = NULL; + int i = 0; + struct char_dev *char_dev = NULL; + char dev_file[DEV_NAME_LEN + 5]; + unsigned char dev_type = 0; + + ENTER(); + + priv->bt_dev.tx_dnld_rdy = TRUE; + if (priv->fw_reload) { + bt_reinit_fw(priv); + LEAVE(); + return ret; + } + + if (drv_mode & DRV_MODE_BT) { + mbt_dev = alloc_mbt_dev(); + if (!mbt_dev) { + PRINTM(FATAL, "Can not allocate mbt dev\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + init_m_dev(&(priv->bt_dev.m_dev[BT_SEQ])); + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE; + priv->bt_dev.m_dev[BT_SEQ].spec_type = IANYWHERE_SPEC; + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)mbt_dev; + priv->bt_dev.m_dev[BT_SEQ].driver_data = priv; + priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0; + } + + dev_type = HCI_SDIO; + + if (mbt_dev) + mbt_dev->type = dev_type; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + + if (mbt_dev && priv->bt_dev.devType == DEV_TYPE_AMP) { + mbt_dev->type |= HCI_BT_AMP; + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_AMP_TYPE; + } + /** Process device tree init parameters before register hci device. + * Since uplayer device has not yet registered, no need to block tx queue. + * */ + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } else if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + /* Get FW version */ + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); + + if (mbt_dev) { + /** init mbt_dev */ + mbt_dev->flags = 0; + mbt_dev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); + mbt_dev->esco_type = (ESCO_HV1); + mbt_dev->link_mode = (HCI_LM_ACCEPT); + + mbt_dev->idle_timeout = 0; + mbt_dev->sniff_max_interval = 800; + mbt_dev->sniff_min_interval = 80; + for (i = 0; i < 3; i++) + mbt_dev->reassembly[i] = NULL; + atomic_set(&mbt_dev->promisc, 0); + + /** alloc char dev node */ + char_dev = alloc_char_dev(); + if (!char_dev) { + class_destroy(chardev_class); + ret = -ENOMEM; + goto err_kmalloc; + } + char_dev->minor = MBTCHAR_MINOR_BASE + mbtchar_minor; + if (mbt_dev->type & HCI_BT_AMP) + char_dev->dev_type = BT_AMP_TYPE; + else + char_dev->dev_type = BT_TYPE; + + if (bt_name) + snprintf(mbt_dev->name, sizeof(mbt_dev->name), "%s%d", + bt_name, mbtchar_minor); + else + snprintf(mbt_dev->name, sizeof(mbt_dev->name), + "mbtchar%d", mbtchar_minor); + snprintf(dev_file, sizeof(dev_file), "/dev/%s", mbt_dev->name); + mbtchar_minor++; + PRINTM(MSG, "BT: Create %s\n", dev_file); + + /** register m_dev to BT char device */ + priv->bt_dev.m_dev[BT_SEQ].index = char_dev->minor; + char_dev->m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + + /** create BT char device node */ + register_char_dev(char_dev, chardev_class, MODULE_NAME, + mbt_dev->name); + + /** chmod & chown for BT char device */ + mbtchar_chown(dev_file, AID_SYSTEM, AID_NET_BT_STACK); + mbtchar_chmod(dev_file, 0666); + + /** create proc device */ + snprintf(priv->bt_dev.m_dev[BT_SEQ].name, + sizeof(priv->bt_dev.m_dev[BT_SEQ].name), + mbt_dev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ); + } + + if ((drv_mode & DRV_MODE_NFC) && + (!(priv->bt_dev.devType == DEV_TYPE_AMP)) && + (priv->bt_dev.devFeature & DEV_FEATURE_NFC)) { + + /** alloc nfc_dev */ + nfc_dev = alloc_nfc_dev(); + if (!nfc_dev) { + PRINTM(FATAL, "Can not allocate nfc dev\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + + /** init m_dev */ + init_m_dev(&(priv->bt_dev.m_dev[NFC_SEQ])); + priv->bt_dev.m_dev[NFC_SEQ].dev_type = NFC_TYPE; + priv->bt_dev.m_dev[NFC_SEQ].spec_type = GENERIC_SPEC; + priv->bt_dev.m_dev[NFC_SEQ].dev_pointer = (void *)nfc_dev; + priv->bt_dev.m_dev[NFC_SEQ].driver_data = priv; + priv->bt_dev.m_dev[NFC_SEQ].read_continue_flag = 0; + + /** create char device for NFC */ + char_dev = alloc_char_dev(); + if (!char_dev) { + class_destroy(chardev_class); + ret = -ENOMEM; + goto err_kmalloc; + } + char_dev->minor = NFCCHAR_MINOR_BASE + nfcchar_minor; + char_dev->dev_type = NFC_TYPE; + if (nfc_name) + snprintf(nfc_dev->name, sizeof(nfc_dev->name), "%s%d", + nfc_name, nfcchar_minor); + else + snprintf(nfc_dev->name, sizeof(nfc_dev->name), + "mnfcchar%d", nfcchar_minor); + snprintf(dev_file, sizeof(dev_file), "/dev/%s", nfc_dev->name); + PRINTM(MSG, "BT: Create %s\n", dev_file); + nfcchar_minor++; + + /** register m_dev to NFC char device */ + priv->bt_dev.m_dev[NFC_SEQ].index = char_dev->minor; + char_dev->m_dev = &(priv->bt_dev.m_dev[NFC_SEQ]); + + /** register char dev */ + register_char_dev(char_dev, chardev_class, MODULE_NAME, + nfc_dev->name); + + /** chmod for NFC char device */ + mbtchar_chmod(dev_file, 0666); + + /** create proc device */ + snprintf(priv->bt_dev.m_dev[NFC_SEQ].name, + sizeof(priv->bt_dev.m_dev[NFC_SEQ].name), + nfc_dev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[NFC_SEQ]), NFC_SEQ); + } + + if ((debug_intf) && ((drv_mode & DRV_MODE_BT) + || (drv_mode & DRV_MODE_NFC) + )) { + /** alloc debug_dev */ + debug_dev = alloc_debug_dev(); + if (!debug_dev) { + PRINTM(FATAL, "Can not allocate debug dev\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + + /** init m_dev */ + init_m_dev(&(priv->bt_dev.m_dev[DEBUG_SEQ])); + priv->bt_dev.m_dev[DEBUG_SEQ].dev_type = DEBUG_TYPE; + priv->bt_dev.m_dev[DEBUG_SEQ].spec_type = GENERIC_SPEC; + priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = (void *)debug_dev; + priv->bt_dev.m_dev[DEBUG_SEQ].driver_data = priv; + + /** create char device for Debug */ + char_dev = alloc_char_dev(); + if (!char_dev) { + class_destroy(chardev_class); + ret = -ENOMEM; + goto err_kmalloc; + } + char_dev->minor = DEBUGCHAR_MINOR_BASE + debugchar_minor; + char_dev->dev_type = DEBUG_TYPE; + if (debug_name) + snprintf(debug_dev->name, sizeof(debug_dev->name), + "%s%d", debug_name, debugchar_minor); + else + snprintf(debug_dev->name, sizeof(debug_dev->name), + "mdebugchar%d", debugchar_minor); + snprintf(dev_file, sizeof(dev_file), "/dev/%s", + debug_dev->name); + PRINTM(MSG, "BT: Create %s\n", dev_file); + debugchar_minor++; + + /** register char dev */ + priv->bt_dev.m_dev[DEBUG_SEQ].index = char_dev->minor; + char_dev->m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + register_char_dev(char_dev, chardev_class, MODULE_NAME, + debug_dev->name); + + /** chmod for debug char device */ + mbtchar_chmod(dev_file, 0666); + + /** create proc device */ + snprintf(priv->bt_dev.m_dev[DEBUG_SEQ].name, + sizeof(priv->bt_dev.m_dev[DEBUG_SEQ].name), + debug_dev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[DEBUG_SEQ]), DEBUG_SEQ); + } + +done: + LEAVE(); + return ret; +err_kmalloc: + LEAVE(); + return ret; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the bt_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to bt_private structure + */ + +bt_private * +bt_add_card(void *card) +{ + bt_private *priv = NULL; + int index = 0; + + ENTER(); + + priv = bt_alloc_priv(); + if (!priv) { + PRINTM(FATAL, "Can not allocate priv\n"); + LEAVE(); + return NULL; + } + /* Save the handle */ + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == NULL) + break; + } + if (index < MAX_BT_ADAPTER) { + m_priv[index] = priv; + } else { + PRINTM(ERROR, "Exceeded maximum cards supported!\n"); + goto err_kmalloc; + } + /* allocate buffer for bt_adapter */ + priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL); + if (!priv->adapter) { + PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n"); + goto err_kmalloc; + } + priv->adapter->tx_buffer = + kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->tx_buffer) { + PRINTM(FATAL, "Allocate buffer for transmit\n"); + goto err_kmalloc; + } + priv->adapter->tx_buf = + (u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT); + priv->adapter->hw_regs_buf = + kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->hw_regs_buf) { + PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n"); + goto err_kmalloc; + } + priv->adapter->hw_regs = + (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT); + bt_init_adapter(priv); + + PRINTM(INFO, "Starting kthread...\n"); + priv->MainThread.priv = priv; + spin_lock_init(&priv->driver_lock); + + bt_create_thread(bt_service_main_thread, &priv->MainThread, + "bt_main_service"); + + /* wait for mainthread to up */ + while (!priv->MainThread.pid) + os_sched_timeout(1); + + sdio_update_card_type(priv, card); + /* Update driver version */ + if (priv->card_type == CARD_TYPE_SD8787) + memcpy(mbt_driver_version, CARD_SD8787, strlen(CARD_SD8787)); + else if (priv->card_type == CARD_TYPE_SD8777) + memcpy(mbt_driver_version, CARD_SD8777, strlen(CARD_SD8777)); + else if (priv->card_type == CARD_TYPE_SD8887) + memcpy(mbt_driver_version, CARD_SD8887, strlen(CARD_SD8887)); + else if (priv->card_type == CARD_TYPE_SD8897) + memcpy(mbt_driver_version, CARD_SD8897, strlen(CARD_SD8897)); + else if (priv->card_type == CARD_TYPE_SD8797) + memcpy(mbt_driver_version, CARD_SD8797, strlen(CARD_SD8797)); + else if (priv->card_type == CARD_TYPE_SD8977) + memcpy(mbt_driver_version, CARD_SD8977, strlen(CARD_SD8977)); + else if (priv->card_type == CARD_TYPE_SD8997) + memcpy(mbt_driver_version, CARD_SD8997, strlen(CARD_SD8997)); + else if (priv->card_type == CARD_TYPE_SD8987) + memcpy(mbt_driver_version, CARD_SD8987, strlen(CARD_SD8987)); + + if (BT_STATUS_SUCCESS != sdio_get_sdio_device(priv)) + goto err_kmalloc; + + /** user config file */ + init_waitqueue_head(&priv->init_user_conf_wait_q); + + priv->bt_dev.card = card; + + ((struct sdio_mmc_card *)card)->priv = priv; + priv->adapter->sd_ireg = 0; + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (sbi_register_dev(priv) < 0) { + PRINTM(FATAL, "Failed to register bt device!\n"); + goto err_registerdev; + } + if (bt_init_fw(priv)) { + PRINTM(FATAL, "BT Firmware Init Failed\n"); + goto err_init_fw; + } + LEAVE(); + return priv; + +err_init_fw: + clean_up_m_devs(priv); + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); +err_registerdev: + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); +err_kmalloc: + if (priv->adapter) + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return NULL; +} + +/** + * @brief This function send hardware remove event + * + * @param priv A pointer to bt_private + * @return N/A + */ +void +bt_send_hw_remove_event(bt_private *priv) +{ + struct sk_buff *skb = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + ENTER(); + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_HARDWARE_REMOVE 0x24 + skb = bt_skb_alloc(3, GFP_ATOMIC); + skb->data[0] = HCI_HARDWARE_ERROR_EVT; + skb->data[1] = 1; + skb->data[2] = HCI_HARDWARE_REMOVE; + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 3); + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + PRINTM(MSG, "Send HW ERROR event\n"); + if (!mdev_recv_frame(skb)) { +#define RX_WAIT_TIMEOUT 300 + mdev_bt->wait_rx_complete = TRUE; + mdev_bt->rx_complete_flag = FALSE; + if (os_wait_interruptible_timeout + (mdev_bt->rx_wait_q, mdev_bt->rx_complete_flag, + RX_WAIT_TIMEOUT)) + PRINTM(MSG, "BT stack received the event\n"); + mdev_bt->stat.byte_rx += 3; + } + } + LEAVE(); + return; +} + +#ifdef BLE_WAKEUP +/** + * @brief This function used to config BLE wakeup pattern + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +int +bt_config_ble_wakeup(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + u16 ocf, left_len; + u8 len, more_cmd; + u8 *pos; + + ENTER(); + + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT: no ble wakeup parameters found\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Config ble wakeup pattern\n"); + + pos = priv->ble_wakeup_buf; + left_len = *(u16 *) pos; + left_len = __le16_to_cpu(left_len); + pos += sizeof(u16); + + while (left_len >= 2) { + more_cmd = *pos; + len = *(pos + 1); + if (((len + 2) > left_len) || + (!more_cmd && ((len + 2) < left_len))) { + PRINTM(ERROR, "Invalid ble parameters\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy(skb_put(skb, len), pos + 2, len); + bt_cb(skb)->pkt_type = *(u8 *)skb->data; + skb_pull(skb, 1); + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data; + ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode); + priv->adapter->cmd_complete = FALSE; + + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, + "BT: Set Set ble wakeup cmd 0x%x timeout:\n", + priv->bt_dev.send_cmd_opcode); + bt_cmd_timeout_func(priv, ocf); + goto done; + } + + pos += len + 2; + left_len -= len + 2; + } + +done: + if (ret != BT_STATUS_SUCCESS) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function send system suspend event + * + * @param priv A pointer to bt_private + * @return BT_STATUS_SUCCESS + */ +int +bt_send_system_event(bt_private *priv, u8 flag) +{ + struct sk_buff *skb = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + + ENTER(); + + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return BT_STATUS_FAILURE; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + + skb = bt_skb_alloc(4, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "Fail to allocate sys suspend event skb\n"); + return BT_STATUS_FAILURE; + } + skb->data[0] = VENDOR_SPECIFIC_EVENT; + skb->data[1] = 2; + skb->data[2] = HCI_SYSTEM_SUSPEND_EVT; + if (flag) + skb->data[3] = HCI_SYSTEM_SUSPEND; + else + skb->data[3] = HCI_SYSTEM_RESUME; + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 4); + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + PRINTM(MSG, "Send system %s event\n", + flag ? "suspend" : "resume"); + if (!mdev_recv_frame(skb)) { +#define RX_WAIT_TIMEOUT 300 + mdev_bt->wait_rx_complete = TRUE; + mdev_bt->rx_complete_flag = FALSE; + if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q, + mdev_bt-> + rx_complete_flag, + RX_WAIT_TIMEOUT)) + PRINTM(MSG, "BT stack received the event\n"); + mdev_bt->stat.byte_rx += 4; + } + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * @return BT_STATUS_SUCCESS + */ +int +bt_remove_card(void *card) +{ + bt_private *priv = (bt_private *)card; + int index; + ENTER(); + if (!priv) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv->adapter->SurpriseRemoved = TRUE; + + bt_send_hw_remove_event(priv); +#ifdef BLE_WAKEUP + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + wake_up_interruptible(&priv->adapter->cmd_wait_q); + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) { + os_sched_timeout(1); + wake_up_interruptible(&priv->MainThread.waitQ); + } + + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); + clean_up_m_devs(priv); + PRINTM(INFO, "Free Adapter\n"); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function initializes module. + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_module(void) +{ + int ret = BT_STATUS_SUCCESS; + int index; + ENTER(); + PRINTM(MSG, "BT: Loading driver\n"); + /* Init the bt_private pointer array first */ + for (index = 0; index < MAX_BT_ADAPTER; index++) + m_priv[index] = NULL; + bt_root_proc_init(); + + /** create char device class */ + chardev_class = class_create(THIS_MODULE, MODULE_NAME); + if (IS_ERR(chardev_class)) { + PRINTM(ERROR, "Unable to allocate class\n"); + bt_root_proc_remove(); + ret = PTR_ERR(chardev_class); + goto done; + } + + if (sbi_register() == NULL) { + bt_root_proc_remove(); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + if (ret) + PRINTM(MSG, "BT: Driver loading failed\n"); + else + PRINTM(MSG, "BT: Driver loaded successfully\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void +bt_exit_module(void) +{ + bt_private *priv; + int index; + ENTER(); + PRINTM(MSG, "BT: Unloading driver\n"); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + priv = m_priv[index]; + if (!priv) + continue; + if (priv && !priv->adapter->SurpriseRemoved) { + if (BT_STATUS_SUCCESS == bt_send_reset_command(priv)) + bt_send_module_cfg_cmd(priv, + MODULE_SHUTDOWN_REQ); + } + sbi_disable_host_int(priv); + + } + + sbi_unregister(); + + bt_root_proc_remove(); + class_destroy(chardev_class); + PRINTM(MSG, "BT: Driver unloaded\n"); + LEAVE(); +} + +module_init(bt_init_module); +module_exit(bt_exit_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +module_param(fw, int, 0); +MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware"); +module_param(fw_crc_check, int, 0); +MODULE_PARM_DESC(fw_crc_check, + "1: Enable FW download CRC check (default); 0: Disable FW download CRC check"); +module_param(psmode, int, 0); +MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode"); +module_param(deep_sleep, int, 0); +MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep"); +#ifdef CONFIG_OF +module_param(dts_enable, int, 0); +MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS"); +#endif +#ifdef DEBUG_LEVEL1 +module_param(mbt_drvdbg, uint, 0); +MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL"); +#endif +#ifdef SDIO_SUSPEND_RESUME +module_param(mbt_pm_keep_power, int, 0); +MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power"); +#endif +module_param(init_cfg, charp, 0); +MODULE_PARM_DESC(init_cfg, "BT init config file name"); +module_param(cal_cfg, charp, 0); +MODULE_PARM_DESC(cal_cfg, "BT calibrate file name"); +module_param(cal_cfg_ext, charp, 0); +MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name"); +module_param(bt_mac, charp, 0660); +MODULE_PARM_DESC(bt_mac, "BT init mac address"); +module_param(drv_mode, int, 0); +MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;" "Bit 2: NFC"); +module_param(bt_name, charp, 0); +MODULE_PARM_DESC(bt_name, "BT interface name"); +module_param(nfc_name, charp, 0); +MODULE_PARM_DESC(nfc_name, "NFC interface name"); +module_param(debug_intf, int, 0); +MODULE_PARM_DESC(debug_intf, + "1: Enable debug interface; 0: Disable debug interface "); +module_param(debug_name, charp, 0); +MODULE_PARM_DESC(debug_name, "Debug interface name"); +module_param(bt_fw_reload, int, 0); +MODULE_PARM_DESC(bt_fw_reload, + "0: disable fw_reload; 1: enable fw reload feature"); +module_param(mbt_gpio_pin, int, 0); +MODULE_PARM_DESC(mbt_gpio_pin, + "GPIO pin to interrupt host. 0xFF: disable GPIO interrupt mode; Others: GPIO pin assigned to generate pulse to host."); +module_param(btindrst, int, 0); +MODULE_PARM_DESC(btindrst, + "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset."); +module_param(btpmic, int, 0); +MODULE_PARM_DESC(btpmic, + "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)"); +module_param(bt_fw_serial, int, 0); +MODULE_PARM_DESC(bt_fw_serial, + "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8897/bt/bt_proc.c b/bt_sd8897/bt/bt_proc.c new file mode 100644 index 0000000..04f87b4 --- /dev/null +++ b/bt_sd8897/bt/bt_proc.c
@@ -0,0 +1,720 @@ +/** @file bt_proc.c + * + * @brief This file handle the functions for proc files + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/proc_fs.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** proc diretory root */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#define PROC_DIR NULL +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#define PROC_DIR (&proc_root) +#else +#define PROC_DIR proc_net +#endif + +/** Proc mbt directory entry */ +static struct proc_dir_entry *proc_mbt; + +#define CMD52_STR_LEN 50 +static char cmd52_string[CMD52_STR_LEN]; + +/** proc data structure */ +struct proc_data { + /** Read length */ + int rdlen; + /** Read buffer */ + char *rdbuf; + /** Write length */ + int wrlen; + /** Maximum write length */ + int maxwrlen; + /** Write buffer */ + char *wrbuf; + /** Private structure */ + struct _bt_private *pbt; + void (*on_close) (struct inode *, struct file *); +}; + +/** Default file permission */ +#define DEFAULT_FILE_PERM 0644 + +/** Bluetooth device offset */ +#define OFFSET_BT_DEV 0x01 +/** Bluetooth adapter offset */ +#define OFFSET_BT_ADAPTER 0x02 +/** Show integer */ +#define SHOW_INT 0x10 +/** Show hex */ +#define SHOW_HEX 0x20 +/** Show string */ +#define SHOW_STRING 0x40 + +/** Device size */ +#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n) +/** Device address */ +#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n) + +/** Adapter size */ +#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n) +/** Adapter address */ +#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n) + +static struct item_data config_items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX} + , +#endif + {"idle_timeout", item_dev_size(idle_timeout), 0, + item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX} + , + {"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap), + OFFSET_BT_DEV | SHOW_HEX} + , + {"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0, + item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX} + , + {"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0, + item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT} + , + {"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode), + OFFSET_BT_DEV | SHOW_INT} + , + +}; + +static struct item_data status_items[] = { + {"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver), + OFFSET_BT_ADAPTER | SHOW_STRING}, + {"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0, + item_dev_addr(tx_dnld_rdy), + OFFSET_BT_DEV | SHOW_INT}, + {"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_state", item_adapter_size(hs_state), 0, + item_adapter_addr(hs_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"ps_state", item_adapter_size(ps_state), 0, + item_adapter_addr(ps_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"WakeupTries", item_adapter_size(WakeupTries), 0, + item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_recv", item_adapter_size(irq_recv), 0, + item_adapter_addr(irq_recv), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_done", item_adapter_size(irq_done), 0, + item_adapter_addr(irq_done), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"skb_pending", item_adapter_size(skb_pending), 0, + item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT}, +}; + +static struct item_data debug_items[] = { + {"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING}, +}; + +/** + * @brief convert string to number + * + * @param s pointer to numbered string + * @return converted number from string s + */ +int +string_to_number(char *s) +{ + int r = 0; + int base = 0; + int pn = 1; + + if (strncmp(s, "-", 1) == 0) { + pn = -1; + s++; + } + if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) { + base = 16; + s += 2; + } else + base = 10; + + for (s = s; *s != 0; s++) { + if ((*s >= '0') && (*s <= '9')) + r = (r * base) + (*s - '0'); + else if ((*s >= 'A') && (*s <= 'F')) + r = (r * base) + (*s - 'A' + 10); + else if ((*s >= 'a') && (*s <= 'f')) + r = (r * base) + (*s - 'a' + 10); + else + break; + } + + return r * pn; +} + +/** + * @brief Create cmd52 string + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +static int +form_cmd52_string(bt_private *priv) +{ + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X", + priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg, + priv->bt_dev.cmd52_val); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/* + * @brief Parse cmd52 string + * + * @param buffer A pointer user buffer + * @param len Length of user buffer + * @param func Parsed func number + * @param reg Parsed reg value + * @param val Parsed value to set + * @return BT_STATUS_SUCCESS + */ +static int +parse_cmd52_string(const char __user * buffer, size_t len, + int *func, int *reg, int *val) +{ + int ret = BT_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + + ENTER(); + + string = kzalloc(CMD52_STR_LEN, GFP_KERNEL); + if (!string) { + PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n"); + LEAVE(); + return -ENOMEM; + } + memcpy(string, buffer + strlen("sdcmd52rw="), + len - strlen("sdcmd52rw=")); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) + *func = string_to_number(pos); + + /* Get reg */ + pos = strsep(&string, " \t"); + if (pos) + *reg = string_to_number(pos); + + /* Get val (optional) */ + pos = strsep(&string, " \t"); + if (pos) + *val = string_to_number(pos); + kfree(string); + LEAVE(); + return ret; +} + +/** + * @brief This function handle generic proc file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS + */ +static int +proc_close(struct inode *inode, struct file *file) +{ + struct proc_data *pdata = file->private_data; + ENTER(); + if (pdata) { + if (pdata->on_close != NULL) + pdata->on_close(inode, file); + kfree(pdata->rdbuf); + kfree(pdata->wrbuf); + kfree(pdata); + } + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handle generic proc file read + * + * @param file A pointer to file structure + * @param buffer A pointer to output buffer + * @param len number of byte to read + * @param offset A pointer to offset of file + * @return number of output data + */ +static ssize_t +proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + if ((!pdata->rdbuf) || (pos < 0)) + return -EINVAL; + if (pos >= pdata->rdlen) + return 0; + if (len > pdata->rdlen - pos) + len = pdata->rdlen - pos; + if (copy_to_user(buffer, pdata->rdbuf + pos, len)) + return -EFAULT; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle generic proc file write + * + * @param file A pointer to file structure + * @param buffer A pointer to input buffer + * @param len number of byte to write + * @param offset A pointer to offset of file + * @return number of input data + */ +static ssize_t +proc_write(struct file *file, + const char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + int func = 0, reg = 0, val = 0; + int config_data = 0; + char *line = NULL; + + if (!pdata->wrbuf || (pos < 0)) + return -EINVAL; + if (pos >= pdata->maxwrlen) + return 0; + if (len > pdata->maxwrlen - pos) + len = pdata->maxwrlen - pos; + if (copy_from_user(pdata->wrbuf + pos, buffer, len)) + return -EFAULT; + if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) { + if (!strncmp + (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) { + line = pdata->wrbuf + pos; + line += strlen("fw_reload") + 1; + config_data = string_to_number(line); + } else + config_data = FW_RELOAD_SDIO_INBAND_RESET; + PRINTM(MSG, "Request fw_reload=%d\n", config_data); + bt_request_fw_reload(pdata->pbt, config_data); + } + if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) { + parse_cmd52_string(pdata->wrbuf + pos, len, &func, ®, &val); + sd_write_cmd52_val(pdata->pbt, func, reg, val); + } + if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) { + bt_dump_sdio_regs(pdata->pbt); + bt_dump_firmware_info_v2(pdata->pbt); + } + + if (pos + len > pdata->wrlen) + pdata->wrlen = len + file->f_pos; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle the generic file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return N/A + */ +static void +proc_on_close(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata = file->private_data; + char *line; + int i; + ENTER(); + if (!pdata->wrlen) + return; + line = pdata->wrbuf; + while (line[0]) { + for (i = 0; i < priv->num_items; i++) { + if (!strncmp + (line, priv->pdata[i].name, + strlen(priv->pdata[i].name))) { + line += strlen(priv->pdata[i].name) + 1; + if (priv->pdata[i].size == 1) + *((u8 *)priv->pdata[i].addr) = + (u8)string_to_number(line); + else if (priv->pdata[i].size == 2) + *((u16 *) priv->pdata[i].addr) = + (u16) string_to_number(line); + else if (priv->pdata[i].size == 4) + *((u32 *)priv->pdata[i].addr) = + (u32)string_to_number(line); + } + } + while (line[0] && line[0] != '\n') + line++; + if (line[0]) + line++; + } + if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd + || priv->pbt->bt_dev.sdio_pull_ctrl + || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) { + bt_prepare_command(priv->pbt); + wake_up_interruptible(&priv->pbt->MainThread.waitQ); + } + LEAVE(); + return; +} + +/** + * @brief This function handle the generic file open + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata; + int i; + char *p; + u32 val = 0; + ENTER(); + priv->pbt->adapter->skb_pending = + skb_queue_len(&priv->pbt->adapter->tx_queue); + file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL); + if (file->private_data == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n"); + LEAVE(); + return -ENOMEM; + } + pdata = (struct proc_data *)file->private_data; + pdata->pbt = priv->pbt; + pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL); + if (pdata->rdbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n"); + kfree(file->private_data); + LEAVE(); + return -ENOMEM; + } + if (priv->fileflag == DEFAULT_FILE_PERM) { + pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL); + if (pdata->wrbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n"); + kfree(pdata->rdbuf); + kfree(file->private_data); + return -ENOMEM; + } + pdata->maxwrlen = priv->bufsize; + pdata->on_close = proc_on_close; + } + p = pdata->rdbuf; + for (i = 0; i < priv->num_items; i++) { + if (priv->pdata[i].size == 1) + val = *((u8 *)priv->pdata[i].addr); + else if (priv->pdata[i].size == 2) + val = *((u16 *) priv->pdata[i].addr); + else if (priv->pdata[i].size == 4) + val = *((u32 *)priv->pdata[i].addr); + if (priv->pdata[i].flag & SHOW_INT) + p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_HEX) + p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_STRING) { + if (!strncmp + (priv->pdata[i].name, "sdcmd52rw", + strlen("sdcmd52rw"))) { + sd_read_cmd52_val(priv->pbt); + form_cmd52_string(priv->pbt); + } + p += sprintf(p, "%s=%s\n", priv->pdata[i].name, + (char *)priv->pdata[i].addr); + } + } + pdata->rdlen = strlen(pdata->rdbuf); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** Proc read ops */ +static const struct file_operations proc_read_ops = { + .read = proc_read, + .open = proc_open, + .release = proc_close +}; + +/** Proc Read-Write ops */ +static const struct file_operations proc_rw_ops = { + .read = proc_read, + .write = proc_write, + .open = proc_open, + .release = proc_close +}; + +static struct proc_private_data proc_files[] = { + {"status", S_IRUGO, 1024, + sizeof(status_items) / sizeof(status_items[0]), + &status_items[0], NULL, &proc_read_ops} + , + {"config", DEFAULT_FILE_PERM, 512, + sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL, + &proc_rw_ops} + , + {"debug", DEFAULT_FILE_PERM, 512, + sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL, + &proc_rw_ops} + , +}; + +/** + * @brief This function initializes proc entry + * + * @param priv A pointer to bt_private structure + * @param m_dev A pointer to struct m_dev + * @param seq Sequence number + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq) +{ + int ret = BT_STATUS_SUCCESS; + struct proc_dir_entry *entry; + int i, j; + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + if (proc_mbt) { + priv->dev_proc[seq].proc_entry = + proc_mkdir(m_dev->name, proc_mbt); + if (!priv->dev_proc[seq].proc_entry) { + PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->dev_proc[seq].pfiles = + kmalloc(sizeof(proc_files), GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles) { + PRINTM(ERROR, + "BT: Could not alloc memory for pfile!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files, + sizeof(proc_files)); + priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files); + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) + priv->dev_proc[seq].pfiles[j].pdata = NULL; + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + priv->dev_proc[seq].pfiles[j].pdata = + kmalloc(priv->dev_proc[seq].pfiles[j]. + num_items * sizeof(struct item_data), + GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles[j].pdata) { + PRINTM(ERROR, + "BT: Could not alloc memory for pdata!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata, + (u8 *)proc_files[j].pdata, + priv->dev_proc[seq].pfiles[j].num_items * + sizeof(struct item_data)); + for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items; + i++) { + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_DEV) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)&priv->bt_dev; + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_ADAPTER) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)priv->adapter; + } + priv->dev_proc[seq].pfiles[j].pbt = priv; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + entry = proc_create_data(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq].proc_entry, + proc_files[j].fops, + &priv->dev_proc[seq]. + pfiles[j]); + if (entry == NULL) +#else + entry = create_proc_entry(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq]. + proc_entry); + if (entry) { + entry->data = &priv->dev_proc[seq].pfiles[j]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + entry->owner = THIS_MODULE; +#endif + entry->proc_fops = proc_files[j].fops; + } else +#endif + PRINTM(MSG, "BT: Fail to create proc %s\n", + proc_files[j].name); + } + } +done: + if (ret == BT_STATUS_FAILURE) { + if (priv->dev_proc[seq].proc_entry) { + remove_proc_entry(m_dev->name, proc_mbt); + priv->dev_proc[seq].proc_entry = NULL; + } + if (priv->dev_proc[seq].pfiles) { + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + if (priv->dev_proc[seq].pfiles[j].pdata) { + kfree(priv->dev_proc[seq].pfiles[j]. + pdata); + priv->dev_proc[seq].pfiles[j].pdata = + NULL; + } + } + kfree(priv->dev_proc[seq].pfiles); + priv->dev_proc[seq].pfiles = NULL; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function removes proc interface + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_proc_remove(bt_private *priv) +{ + int j, i; + ENTER(); + PRINTM(INFO, "BT: Remove Proc Interface\n"); + if (proc_mbt) { + for (i = 0; i < MAX_RADIO_FUNC; i++) { + if (!priv->dev_proc[i].proc_entry) + continue; + for (j = 0; j < ARRAY_SIZE(proc_files); j++) { + remove_proc_entry(proc_files[j].name, + priv->dev_proc[i].proc_entry); + } + remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt); + priv->dev_proc[i].proc_entry = NULL; + + if (priv->dev_proc[i].pfiles) { + for (j = 0; + j < priv->dev_proc[i].num_proc_files; + j++) { + if (priv->dev_proc[i].pfiles[j].pdata) { + kfree(priv->dev_proc[i]. + pfiles[j].pdata); + priv->dev_proc[i].pfiles[j]. + pdata = NULL; + } + } + kfree(priv->dev_proc[i].pfiles); + priv->dev_proc[i].pfiles = NULL; + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function creates proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_root_proc_init(void) +{ + PRINTM(INFO, "BT: Create Proc Interface\n"); + proc_mbt = proc_mkdir("mbt", PROC_DIR); + if (!proc_mbt) { + PRINTM(ERROR, "BT: Cannot create proc interface\n"); + return BT_STATUS_FAILURE; + } + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function removes proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS + */ +int +bt_root_proc_remove(void) +{ + remove_proc_entry("mbt", PROC_DIR); + proc_mbt = NULL; + return BT_STATUS_SUCCESS; +}
diff --git a/bt_sd8897/bt/bt_sdio.h b/bt_sd8897/bt/bt_sdio.h new file mode 100644 index 0000000..8e18b7c --- /dev/null +++ b/bt_sd8897/bt/bt_sdio.h
@@ -0,0 +1,429 @@ +/** @file bt_sdio.h + * @brief This file contains SDIO (interface) module + * related macros, enum, and structure. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_SDIO_H_ +#define _BT_SDIO_H_ + +#include <linux/irqreturn.h> + +/** SD8787 card type */ +#define CARD_TYPE_SD8787 0x01 +/** SD8777 card type */ +#define CARD_TYPE_SD8777 0x02 +/** SD8887 card type */ +#define CARD_TYPE_SD8887 0x03 +/** SD8897 card type */ +#define CARD_TYPE_SD8897 0x04 +/** SD8797 card type */ +#define CARD_TYPE_SD8797 0x05 +/** SD8977 card type */ +#define CARD_TYPE_SD8977 0x06 +/** SD8997 card type */ +#define CARD_TYPE_SD8997 0x07 +/** SD8987 card type */ +#define CARD_TYPE_SD8987 0x08 + +/** IRQ return type */ +typedef irqreturn_t IRQ_RET_TYPE; +/** IRQ return */ +#define IRQ_RET (return IRQ_HANDLED) +/** ISR notifier function */ +typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id, + struct pt_regs * reg); + +/** SDIO header length */ +#define SDIO_HEADER_LEN 4 + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */ +#define GPIO_INT_NEW_MODE 255 +/* SD block size can not bigger than 64 due to buf size limit in firmware */ +/** define SD block size for data Tx/Rx */ +#define SD_BLOCK_SIZE 64 +/** define SD block size for firmware download */ +#define SD_BLOCK_SIZE_FW_DL 256 + +/** Number of blocks for firmware transfer */ +#define FIRMWARE_TRANSFER_NBLOCK 2 + +/** Firmware ready */ +#define FIRMWARE_READY 0xfedc + +/* Bus Interface Control Reg 0x07 */ +/** SD BUS width 1 */ +#define SD_BUS_WIDTH_1 0x00 +/** SD BUS width 4 */ +#define SD_BUS_WIDTH_4 0x02 +/** SD BUS width mask */ +#define SD_BUS_WIDTH_MASK 0x03 +/** Asynchronous interrupt mode */ +#define ASYNC_INT_MODE 0x20 + +/** magic register */ +#define CARD_MAGIC_REG 0xF0 +/** magic value */ +#define MAGIC_VAL 0x24 + +/* Host Control Registers */ +/** Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/** Host Control Registers : Host without Command 53 finish host*/ +#define HOST_TO_CARD_EVENT (0x1U << 3) +/** Host Control Registers : Host terminates Command 53 */ +#define HOST_TERM_CMD53 (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Host interrupt RSR */ +#define HOST_INT_RSR_REG 0x01 + +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x02 + +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +#define HOST_INTSTATUS_REG 0x03 +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/ +#define ENABLE_GPIO_1_INT_MODE 0x88 +/** Scratch reg 3 2 : Configure GPIO-1 INT*/ +#define SCRATCH_REG_32 0xEE + +/** Host Control Registers : Host interrupt status */ +#define HOST_INT_STATUS_REG 0x28 +/** Host Control Registers : Upload CRC error */ +#define UP_LD_CRC_ERR (0x1U << 2) +/** Host Control Registers : Upload restart */ +#define UP_LD_RESTART (0x1U << 1) +/** Host Control Registers : Download restart */ +#define DN_LD_RESTART (0x1U << 0) + +/* Card Control Registers */ +/** Card Control Registers : Read SQ base address A0 register */ +#define SQ_READ_BASE_ADDRESS_A0_REG 0x40 +/** Card Control Registers : Read SQ base address A1 register */ +#define SQ_READ_BASE_ADDRESS_A1_REG 0x41 +/** Card Control Registers : Read SQ base address A2 register */ +#define SQ_READ_BASE_ADDRESS_A2_REG 0x42 +/** Card Control Registers : Read SQ base address A3 register */ +#define SQ_READ_BASE_ADDRESS_A3_REG 0x43 +/** Card Control Registers : Read SQ base address B0 register */ +#define SQ_READ_BASE_ADDRESS_B0_REG 0x44 +/** Card Control Registers : Read SQ base address B1 register */ +#define SQ_READ_BASE_ADDRESS_B1_REG 0x45 +/** Card Control Registers : Read SQ base address B2 register */ +#define SQ_READ_BASE_ADDRESS_B2_REG 0x46 +/** Card Control Registers : Read SQ base address B3 register */ +#define SQ_READ_BASE_ADDRESS_B3_REG 0x47 + +/** Card Control Registers : Card status register */ +#define CARD_STATUS_REG 0x30 +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host interrupt mask register */ +#define HOST_INTERRUPT_MASK_REG 0x34 +/** Card Control Registers : Host power interrupt mask */ +#define HOST_POWER_INT_MASK (0x1U << 3) +/** Card Control Registers : Abort card interrupt mask */ +#define ABORT_CARD_INT_MASK (0x1U << 2) +/** Card Control Registers : Upload card interrupt mask */ +#define UP_LD_CARD_INT_MASK (0x1U << 1) +/** Card Control Registers : Download card interrupt mask */ +#define DN_LD_CARD_INT_MASK (0x1U << 0) + +/** Card Control Registers : Card interrupt status register */ +#define CARD_INTERRUPT_STATUS_REG 0x38 +/** Card Control Registers : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** Card Control Registers : Card interrupt RSR register */ +#define CARD_INTERRUPT_RSR_REG 0x3c +/** Card Control Registers : Power up RSR */ +#define POWER_UP_RSR (0x1U << 4) +/** Card Control Registers : Power down RSR */ +#define POWER_DOWN_RSR (0x1U << 3) + +/** Card Control Registers : Debug 0 register */ +#define DEBUG_0_REG 0x70 +/** Card Control Registers : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : Debug 1 register */ +#define DEBUG_1_REG 0x71 +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : Debug 2 register */ +#define DEBUG_2_REG 0x72 +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : Debug 3 register */ +#define DEBUG_3_REG 0x73 +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0x78 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0x79 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0x7A + +/** Firmware status 0 register */ +#define CARD_FW_STATUS0_REG 0x60 +/** Firmware status 1 register */ +#define CARD_FW_STATUS1_REG 0x61 +/** Rx length register */ +#define CARD_RX_LEN_REG 0x62 +/** Rx unit register */ +#define CARD_RX_UNIT_REG 0x63 +/** Card Control Registers : Miscellaneous Configuration Register */ +#define CARD_MISC_CFG_REG 0x6C +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT (0x1U << 4) + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0x68 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0x69 +/** Card Control Registers : Card OCR 3 register */ +#define CARD_OCR_3_REG 0x6A +/** Card Control Registers : Card config register */ +#define CARD_CONFIG_REG 0x6B +/** Card Control Registers : Card revision register */ +#define CARD_REVISION_REG 0x5c +/** Card Control Registers : Command 53 finish G BUS */ +#define CMD53_FINISH_GBUS (0x1U << 1) +/** Card Control Registers : SD negative edge */ +#define SD_NEG_EDGE (0x1U << 0) + +/* Special registers in function 0 of the SDxx card */ +/** Special register in function 0 of the SDxxx card : Scratch 0 */ +#define SCRATCH_0_REG 0x80fe +/** Special register in function 0 of the SDxxx card : Scratch 1 */ +#define SCRATCH_1_REG 0x80ff +/** Host F1 read base 0 */ +#define HOST_F1_RD_BASE_0 0x0040 +/** Host F1 read base 1 */ +#define HOST_F1_RD_BASE_1 0x0041 +/** Host F1 card ready */ +#define HOST_F1_CARD_RDY 0x0020 + +/** Chip Id Register 0 */ +#define CARD_CHIP_ID_0_REG 0x801c +/** Chip Id Register 1 */ +#define CARD_CHIP_ID_1_REG 0x801d + +struct sdio_mmc_card { + /** sdio_func structure pointer */ + struct sdio_func *func; + /** bt_private structure pointer */ + bt_private *priv; +}; + +struct sdio_card_reg { + u8 cfg; + u8 host_int_mask; // HOST_INT_MASK_REG + u8 host_intstatus; // HOST_INTSTATUS_REG + u8 host_int_rsr_reg; // HOST_INT_RSR_REG + u8 card_misc_cfg_reg; // CARD_MISC_CFG_REG + u8 card_status; // CARD_STATUS_REG + u8 sq_read_base_addr_a0; // SQ_READ_BASE_ADDRESS_A0_REG + u8 sq_read_base_addr_a1; // SQ_READ_BASE_ADDRESS_A1_REG + u8 card_revision; // CARD_REVISION_REG + u8 card_fw_status0; // CARD_FW_STATUS0_REG + u8 card_fw_status1; // CARD_FW_STATUS1_REG + u8 card_rx_len; // CARD_RX_LEN_REG + u8 card_rx_unit; // CARD_RX_UNIT_REG + u8 io_port_0; // IO_PORT_0_REG + u8 io_port_1; // IO_PORT_1_REG + u8 io_port_2; // IO_PORT_2_REG +}; + +struct sdio_device { + const struct sdio_card_reg *reg; +}; + +static const struct sdio_card_reg bt_reg_87xx = { + .cfg = 0x00, + .host_int_mask = 0x02, // HOST_INT_MASK_REG + .host_intstatus = 0x03, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x01, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0x6c, // CARD_MISC_CFG_REG + .card_status = 0x30, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x40, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x41, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0x5C, // CARD_REVISION_REG + .card_fw_status0 = 0x60, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0x61, // CARD_FW_STATUS1_REG + .card_rx_len = 0x62, // CARD_RX_LEN_REG + .card_rx_unit = 0x63, // CARD_RX_UNIT_REG + .io_port_0 = 0x78, // IO_PORT_0_REG + .io_port_1 = 0x79, // IO_PORT_1_REG + .io_port_2 = 0x7a, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8887 = { + .cfg = 0x00, + .host_int_mask = 0x08, // HOST_INT_MASK_REG + .host_intstatus = 0x0C, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x04, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xD8, // CARD_MISC_CFG_REG + .card_status = 0x5C, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x6C, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x6D, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xC8, // CARD_REVISION_REG + .card_fw_status0 = 0x88, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0x89, // CARD_FW_STATUS1_REG + .card_rx_len = 0x8A, // CARD_RX_LEN_REG + .card_rx_unit = 0x8B, // CARD_RX_UNIT_REG + .io_port_0 = 0xE4, // IO_PORT_0_REG + .io_port_1 = 0xE5, // IO_PORT_1_REG + .io_port_2 = 0xE6, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8897 = { + .cfg = 0x00, + .host_int_mask = 0x02, // HOST_INT_MASK_REG + .host_intstatus = 0x03, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x01, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xCC, // CARD_MISC_CFG_REG + .card_status = 0x50, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x60, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x61, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xBC, // CARD_REVISION_REG + .card_fw_status0 = 0xC0, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0xC1, // CARD_FW_STATUS1_REG + .card_rx_len = 0xC2, // CARD_RX_LEN_REG + .card_rx_unit = 0xC3, // CARD_RX_UNIT_REG + .io_port_0 = 0xD8, // IO_PORT_0_REG + .io_port_1 = 0xD9, // IO_PORT_1_REG + .io_port_2 = 0xDA, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8977_8997 = { + .cfg = 0x00, + .host_int_mask = 0x08, // HOST_INT_MASK_REG + .host_intstatus = 0x0C, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x04, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xD8, // CARD_MISC_CFG_REG + .card_status = 0x5C, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0xF8, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0xF9, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xC8, // CARD_REVISION_REG + .card_fw_status0 = 0xE8, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0xE9, // CARD_FW_STATUS1_REG + .card_rx_len = 0xEA, // CARD_RX_LEN_REG + .card_rx_unit = 0xEB, // CARD_RX_UNIT_REG + .io_port_0 = 0xE4, // IO_PORT_0_REG + .io_port_1 = 0xE5, // IO_PORT_1_REG + .io_port_2 = 0xE6, // IO_PORT_2_REG +}; + +static const struct sdio_device bt_sdio_sd8787 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8777 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8887 = { + .reg = &bt_reg_8887, +}; + +static const struct sdio_device bt_sdio_sd8897 = { + .reg = &bt_reg_8897, +}; + +static const struct sdio_device bt_sdio_sd8797 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8977 = { + .reg = &bt_reg_8977_8997, +}; + +static const struct sdio_device bt_sdio_sd8997 = { + .reg = &bt_reg_8977_8997, +}; + +static const struct sdio_device bt_sdio_sd8987 = { + .reg = &bt_reg_8977_8997, +}; + +/** DMA alignment value */ +#define DMA_ALIGNMENT 64 +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** This function read cmd52 register */ +int sd_write_reg(bt_private *priv, int reg, u8 val); +/** This function write cmd52 value to register */ +int sd_read_reg(bt_private *priv, int reg, u8 *data); +/** This function reads the Cmd52 value in dev structure */ +int sd_read_cmd52_val(bt_private *priv); +/** This function updates card reg based on the Cmd52 value in dev structure */ +int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val); + +void sdio_update_card_type(bt_private *priv, void *card); +int sdio_get_sdio_device(bt_private *priv); + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** This function tells lower driver that BT is suspended */ +void bt_is_suspended(bt_private *priv); +#endif +#endif +#endif +#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8897/bt/bt_sdiommc.c b/bt_sd8897/bt/bt_sdiommc.c new file mode 100644 index 0000000..83a76fc --- /dev/null +++ b/bt_sd8897/bt/bt_sdiommc.c
@@ -0,0 +1,2355 @@ +/** @file bt_sdiommc.c + * @brief This file contains SDIO IF (interface) module + * related functions. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/firmware.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/card.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** define marvell vendor id */ +#define MARVELL_VENDOR_ID 0x02df + +/** Max retry number of CMD53 read/write */ +#define MAX_CMD53_RETRY 3 +/** Max retry number of CMD53 read/write */ +#define MAX_CMD52_RETRY 3 +/** Firmware name */ +static char *fw_name; +/** fw serial download flag */ +extern int bt_fw_serial; +int bt_intmode = INT_MODE_SDIO; +/** request firmware nowait */ +int bt_req_fw_nowait; +static int multi_fn = BIT(2); + +#define DEFAULT_FW_NAME "" + +/** FW header length for CRC check disable */ +#define FW_CRC_HEADER_RB2 28 +/** FW header for CRC check disable */ +static u8 fw_crc_header_rb_2[FW_CRC_HEADER_RB2] = { + 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x9d, 0x32, 0xbb, 0x11, 0x01, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x67, 0xd6, 0xfc, 0x25 +}; + +/** FW header length for CRC check disable */ +#define FW_CRC_HEADER_RB 24 +/** FW header for CRC check disable */ +static u8 fw_crc_header_rb_1[FW_CRC_HEADER_RB] = { + 0x01, 0x00, 0x00, 0x00, 0x04, 0xfd, 0x00, 0x04, + 0x08, 0x00, 0x00, 0x00, 0x26, 0x52, 0x2a, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** Default firmware name */ +#define DEFAULT_FW_NAME_8777 "mrvl/sd8777_uapsta.bin" +#define DEFAULT_FW_NAME_8787 "mrvl/sd8787_uapsta.bin" +#define DEFAULT_FW_NAME_8797 "mrvl/sd8797_uapsta.bin" +#define DEFAULT_FW_NAME_8887 "mrvl/sd8887_uapsta.bin" +#define DEFAULT_FW_NAME_8897 "mrvl/sd8897_uapsta.bin" +#define DEFAULT_FW_NAME_8977 "mrvl/sdsd8977_combo.bin" +#define DEFAULT_FW_NAME_8997 "mrvl/sdsd8997_combo.bin" + +/** SD8787 chip revision ID */ +#define SD8787_W0 0x30 +#define SD8787_W1 0x31 +#define SD8787_A0_A1 0x40 +/** SD8797 chip revision ID */ +#define SD8797_A0 0x00 +#define SD8797_B0 0x10 +/** SD8897 chip revision ID */ +#define SD8897_A0 0x10 +#define SD8897_B0 0x20 + +/** SD8887 chip revision ID */ +#define SD8887_A0 0x0 +#define SD8887_A2 0x2 +#define SD8887_A0_FW_NAME "mrvl/sd8887_uapsta.bin" +#define SD8887_A2_FW_NAME "mrvl/sd8887_uapsta_a2.bin" +#define SD8887_A2_BT_FW_NAME "mrvl/sd8887_bt_a2.bin" + +#define SD8897_A0_FW_NAME "mrvl/sd8897_uapsta_a0.bin" +#define SD8897_B0_FW_NAME "mrvl/sd8897_uapsta.bin" + +#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin" +#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin" +#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin" +#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin" + +/** SD8977 chip revision ID */ +#define SD8977_V0 0x0 +#define SD8977_V1 0x8 +#define SD8977_V2 0x9 +#define SD8977_V0_FW_NAME "mrvl/sdsd8977_combo.bin" +#define SD8977_V0_BT_FW_NAME "mrvl/sd8977_bt.bin" +#define SD8977_V1_FW_NAME "mrvl/sdsd8977_combo_v1.bin" +#define SD8977_V1_BT_FW_NAME "mrvl/sd8977_bt_v1.bin" +#define SD8977_V2_FW_NAME "mrvl/sdsd8977_combo_v2.bin" +#define SD8977_V2_BT_FW_NAME "mrvl/sd8977_bt_v2.bin" + +/** SD8997 chip revision ID */ +#define SD8997_Z 0x02 +#define SD8997_V2 0x10 +#define SD8997_Z_FW_NAME "mrvl/sdsd8997_combo.bin" +#define SD8997_Z_BT_FW_NAME "mrvl/sd8997_bt.bin" +#define SD8997_V2_FW_NAME "mrvl/sdsd8997_combo_v2.bin" +#define SD8997_V2_BT_FW_NAME "mrvl/sd8997_bt_v2.bin" +#define SD8997_V3_FW_NAME "mrvl/sdsd8997_combo_v3.bin" +#define SD8997_V3_BT_FW_NAME "mrvl/sd8997_bt_v3.bin" + +/** SD8987 */ +#define SD8987_FW_NAME "mrvl/sdsd8987_combo.bin" +#define SD8987_BT_FW_NAME "mrvl/sd8987_bt.bin" + +/** Function number 2 */ +#define FN2 2 +/** Device ID for SD8787 FN2 */ +#define SD_DEVICE_ID_8787_BT_FN2 0x911A +/** Device ID for SD8787 FN3 */ +#define SD_DEVICE_ID_8787_BT_FN3 0x911B +/** Device ID for SD8777 FN2 */ +#define SD_DEVICE_ID_8777_BT_FN2 0x9132 +/** Device ID for SD8777 FN3 */ +#define SD_DEVICE_ID_8777_BT_FN3 0x9133 +/** Device ID for SD8887 FN2 */ +#define SD_DEVICE_ID_8887_BT_FN2 0x9136 +/** Device ID for SD8887 FN3 */ +#define SD_DEVICE_ID_8887_BT_FN3 0x9137 +/** Device ID for SD8897 FN2 */ +#define SD_DEVICE_ID_8897_BT_FN2 0x912E +/** Device ID for SD8897 FN3 */ +#define SD_DEVICE_ID_8897_BT_FN3 0x912F +/** Device ID for SD8797 FN2 */ +#define SD_DEVICE_ID_8797_BT_FN2 0x912A +/** Device ID for SD8797 FN3 */ +#define SD_DEVICE_ID_8797_BT_FN3 0x912B +/** Device ID for SD8977 FN2 */ +#define SD_DEVICE_ID_8977_BT_FN2 0x9146 +/** Device ID for SD8997 FN2 */ +#define SD_DEVICE_ID_8997_BT_FN2 0x9142 +/** Device ID for SD8987 FN2 */ +#define SD_DEVICE_ID_8987_BT_FN2 0x914a +/** Device ID for SD8987 FN3 */ +#define SD_DEVICE_ID_8987_BT_FN3 0x914b + +/** Array of SDIO device ids when multi_fn=0x12 */ +static const struct sdio_device_id bt_ids[] = { + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8987_BT_FN2)}, + {} +}; + +MODULE_DEVICE_TABLE(sdio, bt_ids); + +/******************************************************** + Global Variables +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +extern int mbt_pm_keep_power; +#endif + +extern bt_private *m_priv[]; +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function gets rx_unit value + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_get_rx_unit(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_unit_reg = priv->psdio_device->reg->card_rx_unit; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_unit_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + priv->bt_dev.rx_unit = reg; + + LEAVE(); + return ret; +} + +/** + * @brief This function reads fwstatus registers + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_read_firmware_status(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 fws0; + u8 fws1; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0; + u8 card_fw_status1_reg = priv->psdio_device->reg->card_fw_status1; + + ENTER(); + + fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + *dat = (((u16) fws1) << 8) | fws0; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function reads rx length + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sd_read_rx_len(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_len_reg = priv->psdio_device->reg->card_rx_len; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_len_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + *dat = (u16) reg << priv->bt_dev.rx_unit; + + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts mask + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_enable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask; + + ENTER(); + + sdio_writeb(card->func, mask, host_int_mask_reg, &ret); + if (ret) { + PRINTM(WARN, "BT: Unable to enable the host interrupt!\n"); + ret = BT_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** @brief This function disables the host interrupts mask. + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sbi_disable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_FAILURE; + u8 host_int_mask; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask; + + ENTER(); + + /* Read back the host_int_mask register */ + host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret); + if (ret) + goto done; + + /* Update with the mask and write back to the register */ + host_int_mask &= ~mask; + sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret); + if (ret < 0) { + PRINTM(WARN, "BT: Unable to diable the host interrupt!\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function polls the card status register + * + * @param priv A pointer to bt_private structure + * @param bits the bit mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_poll_card_status(bt_private *priv, u8 bits) +{ + int tries; + int rval; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 cs; + u8 card_status_reg = priv->psdio_device->reg->card_status; + + ENTER(); + + for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { + cs = sdio_readb(card->func, card_status_reg, &rval); + if (rval != 0) + break; + if (rval == 0 && (cs & bits) == bits) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + udelay(1); + } + PRINTM(ERROR, + "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n", + rval, tries, cs); + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_cmd52_val(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 func, reg, val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + func = priv->bt_dev.cmd52_func; + reg = priv->bt_dev.cmd52_reg; + sdio_claim_host(card->func); + if (func) + val = sdio_readb(card->func, reg, &ret); + else + val = sdio_f0_readb(card->func, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n", + func, reg); + } else { + priv->bt_dev.cmd52_val = val; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param func Stores func variable + * @param reg Stores reg variable + * @param val Stores val variable + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_cmd52_val(bt_private *priv, int func, int reg, int val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + if (val >= 0) { + /* Perform actual write only if val is provided */ + sdio_claim_host(card->func); + if (func) + sdio_writeb(card->func, val, reg, &ret); + else + sdio_f0_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, + "BT: Cannot write value (0x%x) to func %d reg %d\n", + val, func, reg); + goto done; + } + priv->bt_dev.cmd52_val = val; + } + + /* Save current func and reg for future read */ + priv->bt_dev.cmd52_func = func; + priv->bt_dev.cmd52_reg = reg; + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to write + * @param val value + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_reg(bt_private *priv, int reg, u8 val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + sdio_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to read + * @param data Data + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_reg(bt_private *priv, int reg, u8 *data) +{ + int ret = BT_STATUS_SUCCESS; + u8 val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + *data = val; + LEAVE(); + return ret; +} + +/** + * @brief This function probes the card + * + * @param func A pointer to sdio_func structure. + * @param id A pointer to structure sdio_device_id + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = NULL; + struct sdio_mmc_card *card = NULL; + + ENTER(); + + PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor, + id->device, id->class, func->num); + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto done; + } + card->func = func; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + /* wait for chip fully wake up */ + if (!func->enable_timeout) + func->enable_timeout = 200; +#endif + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) { + sdio_disable_func(func); + sdio_release_host(func); + PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret); + kfree(card); + LEAVE(); + return -EIO; + } + sdio_release_host(func); + priv = bt_add_card(card); + if (!priv) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + ret = BT_STATUS_FAILURE; + kfree(card); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks if the firmware is ready to accept + * command or not. + * + * @param priv A pointer to bt_private structure + * @param pollnum Number of times to poll fw status + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_verify_fw_download(bt_private *priv, int pollnum) +{ + int ret = BT_STATUS_FAILURE; + u16 firmwarestat = 0; + int tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if (sd_read_firmware_status(priv, &firmwarestat) < 0) + continue; + if (firmwarestat == FIRMWARE_READY) { + PRINTM(MSG, "BT FW is active(%d)\n", tries); + ret = BT_STATUS_SUCCESS; + break; + } + mdelay(100); + } + if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) { + PRINTM(ERROR, + "Fail to poll firmware status: firmwarestat=0x%x\n", + firmwarestat); + bt_dump_sdio_regs(priv); + } + LEAVE(); + return ret; +} + +/** + * @brief Transfers firmware to card + * + * @param priv A Pointer to bt_private structure + * @param fw A Pointer to fw image + * @param fw_len fw image len + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len) +{ + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 *firmware = fw; + int firmwarelen = fw_len; + u8 base0; + u8 base1; + int ret = BT_STATUS_SUCCESS; + int offset; + void *tmpfwbuf = NULL; + int tmpfwbufsz; + u8 *fwbuf; + u16 len; + int txlen = 0; + int tx_blocks = 0; + int i = 0; + int tries = 0; +#ifdef FW_DOWNLOAD_SPEED + u32 tv1, tv2; +#endif + u8 sq_read_base_address_a0_reg = + priv->psdio_device->reg->sq_read_base_addr_a0; + u8 sq_read_base_address_a1_reg = + priv->psdio_device->reg->sq_read_base_addr_a1; + u8 crc_buffer = 0; + u8 *header_crc_fw = NULL; + u8 header_crc_fw_len = 0; + + if (priv->card_type == CARD_TYPE_SD8787) { + header_crc_fw = fw_crc_header_rb_1; + header_crc_fw_len = FW_CRC_HEADER_RB; + } else if (priv->card_type == CARD_TYPE_SD8777) { + header_crc_fw = fw_crc_header_rb_2; + header_crc_fw_len = FW_CRC_HEADER_RB2; + } + + ENTER(); + + PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen); + +#ifdef FW_DOWNLOAD_SPEED + tv1 = get_utimeofday(); +#endif + + tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT; + tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); + if (!tmpfwbuf) { + PRINTM(ERROR, + "BT: Unable to allocate buffer for firmware. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + /* Ensure aligned firmware buffer */ + fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); + + if (!(priv->fw_crc_check) + && ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + ) { + /* CRC check not required, use custom header first */ + firmware = header_crc_fw; + firmwarelen = header_crc_fw_len; + crc_buffer = 1; + } + + /* Perform firmware data transfer */ + offset = 0; + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits + */ + ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret < 0) { + PRINTM(FATAL, + "BT: FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + if (!crc_buffer) + /* More data? */ + if (offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + base0 = sdio_readb(card->func, + sq_read_base_address_a0_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", + base0, base0); + ret = BT_STATUS_FAILURE; + goto done; + } + base1 = sdio_readb(card->func, + sq_read_base_address_a1_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download\n", + base1, base1); + ret = BT_STATUS_FAILURE; + goto done; + } + len = (((u16) base1) << 8) | base0; + + if (len != 0) + break; + udelay(10); + } + + if (len == 0) + break; + else if (len > BT_UPLD_SIZE) { + PRINTM(FATAL, + "BT: FW download failure @ %d, invalid length %d\n", + offset, len); + ret = BT_STATUS_FAILURE; + goto done; + } + /** ignore CRC check before download the first packet */ + if (offset == 0 && (len & BIT(0))) + len &= ~BIT(0); + txlen = len; + + if (len & BIT(0)) { + i++; + if (i >= MAX_CMD53_RETRY) { + PRINTM(FATAL, + "BT: FW download failure @ %d, over max retry count\n", + offset); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: FW CRC error indicated by the helper:" + " len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + + PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last block */ + if (firmwarelen - offset < txlen) + txlen = firmwarelen - offset; + + PRINTM(INFO, "."); + + tx_blocks = + (txlen + SD_BLOCK_SIZE_FW_DL - + 1) / SD_BLOCK_SIZE_FW_DL; + + /* Copy payload to buffer */ + memcpy(fwbuf, &firmware[offset], txlen); + } + + /* Send data */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf, + tx_blocks * SD_BLOCK_SIZE_FW_DL); + + if (ret < 0) { + PRINTM(ERROR, + "BT: FW download, write iomem (%d) failed @ %d\n", + i, offset); + sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret); + if (ret) + PRINTM(ERROR, "write ioreg failed (CFG)\n"); + } + + offset += txlen; + if (crc_buffer + && ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + ) { + if (offset >= header_crc_fw_len) { + /* Custom header download complete, restore + original FW */ + offset = 0; + firmware = fw; + firmwarelen = fw_len; + crc_buffer = 0; + } + } + } while (TRUE); + + PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset); + + ret = BT_STATUS_SUCCESS; +done: +#ifdef FW_DOWNLOAD_SPEED + tv2 = get_utimeofday(); + PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000, + (tv1 % 1000000) / 1000, tv1 % 1000); + PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); + tv2 -= tv1; + PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); +#endif + kfree(tmpfwbuf); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * + * @param fw_firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_request_fw_dpc(const struct firmware *fw_firmware, void *context) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = (bt_private *)context; + struct sdio_mmc_card *card = NULL; + struct m_dev *m_dev_bt = NULL; + struct m_dev *m_dev_nfc = NULL; + struct timeval tstamp; + int index; + + ENTER(); + + m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ]; + m_dev_nfc = &priv->bt_dev.m_dev[NFC_SEQ]; + + if ((priv == NULL) || (priv->adapter == NULL) || + (priv->bt_dev.card == NULL) || (m_dev_bt == NULL) + || (m_dev_nfc == NULL) + ) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + card = (struct sdio_mmc_card *)priv->bt_dev.card; + + if (!fw_firmware) { + do_gettimeofday(&tstamp); + if (tstamp.tv_sec > + (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(ERROR, + "BT: No firmware image found. Skipping download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: No firmware image found! Retrying download\n"); + /* Wait a second here before calling the callback again */ + os_sched_timeout(1000); + sd_download_firmware_w_helper(priv); + LEAVE(); + return ret; + } + + priv->firmware = fw_firmware; + + if (BT_STATUS_FAILURE == + sd_init_fw_dpc(priv, (u8 *)priv->firmware->data, + priv->firmware->size)) { + PRINTM(ERROR, + "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n", + bt_req_fw_nowait); + sdio_release_host(card->func); + ret = BT_STATUS_FAILURE; + goto done; + } + + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) { + PRINTM(ERROR, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + sdio_release_host(card->func); + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + LEAVE(); + return ret; + +done: + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + /* For synchronous download cleanup will be done in add_card */ + if (!bt_req_fw_nowait) + return ret; + PRINTM(INFO, "unregister device\n"); + sbi_unregister_dev(priv); + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); + bt_proc_remove(priv); + clean_up_m_devs(priv); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return None + **/ +static void +sd_request_fw_callback(const struct firmware *firmware, void *context) +{ + ENTER(); + sd_request_fw_dpc(firmware, context); + LEAVE(); + return; +} + +/** + * @brief This function downloads firmware image to the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sd_download_firmware_w_helper(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + int err; + char *cur_fw_name = NULL; + + ENTER(); + + cur_fw_name = fw_name; + + if (fw_name == NULL) { + if (priv->card_type == CARD_TYPE_SD8787) + cur_fw_name = DEFAULT_FW_NAME_8787; + else if (priv->card_type == CARD_TYPE_SD8777) + cur_fw_name = DEFAULT_FW_NAME_8777; + else if (priv->card_type == CARD_TYPE_SD8887) { + /* Check revision ID */ + switch (priv->adapter->chip_rev) { + case SD8887_A0: + cur_fw_name = SD8887_A0_FW_NAME; + break; + case SD8887_A2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8887_A2_FW_NAME; + else + cur_fw_name = SD8887_A2_BT_FW_NAME; + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8887; + break; + } + } else if (priv->card_type == CARD_TYPE_SD8897) + cur_fw_name = DEFAULT_FW_NAME_8897; + else if (priv->card_type == CARD_TYPE_SD8797) + cur_fw_name = DEFAULT_FW_NAME_8797; + else if (priv->card_type == CARD_TYPE_SD8977) { + switch (priv->adapter->chip_rev) { + case SD8977_V0: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V0_FW_NAME; + else + cur_fw_name = SD8977_V0_BT_FW_NAME; + break; + case SD8977_V1: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V1_FW_NAME; + else + cur_fw_name = SD8977_V1_BT_FW_NAME; + break; + case SD8977_V2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V2_FW_NAME; + else + cur_fw_name = SD8977_V2_BT_FW_NAME; + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8977; + break; + } + } else if (priv->card_type == CARD_TYPE_SD8997) + switch (priv->adapter->chip_rev) { + case SD8997_Z: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8997_Z_FW_NAME; + else + cur_fw_name = SD8997_Z_BT_FW_NAME; + break; + case SD8997_V2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) { + if (priv->adapter->magic_val == + MAGIC_VAL) + cur_fw_name = SD8997_V3_FW_NAME; + else + cur_fw_name = SD8997_V2_FW_NAME; + } else { + if (priv->adapter->magic_val == + MAGIC_VAL) + cur_fw_name = + SD8997_V3_BT_FW_NAME; + else + cur_fw_name = + SD8997_V2_BT_FW_NAME; + } + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8997; + break; + } else if (priv->card_type == CARD_TYPE_SD8987) { + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8987_FW_NAME; + else + cur_fw_name = SD8987_BT_FW_NAME; + } + } + + PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + GFP_KERNEL, priv, + sd_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#endif +#endif + if (ret < 0) + PRINTM(FATAL, + "BT: request_firmware_nowait() failed, error code = %#x\n", + ret); + } else { + err = request_firmware(&priv->firmware, cur_fw_name, + priv->hotplug_device); + if (err < 0) { + PRINTM(FATAL, + "BT: request_firmware() failed, error code = %#x\n", + err); + ret = BT_STATUS_FAILURE; + } else + ret = sd_request_fw_dpc(priv->firmware, priv); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function reads data from the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_card_to_host(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 buf_len = 0; + int buf_block_len; + int blksz; + struct sk_buff *skb = NULL; + u32 type; + u8 *payload = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + struct m_dev *mdev_nfc = &(priv->bt_dev.m_dev[NFC_SEQ]); + struct nfc_dev *nfc_dev = + (struct nfc_dev *)priv->bt_dev.m_dev[NFC_SEQ].dev_pointer; + struct m_dev *mdev_debug = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + struct debug_dev *debug_dev = + (struct debug_dev *)priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer; + struct sdio_mmc_card *card = priv->bt_dev.card; + int i = 0; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + do { + /* Read the length of data to be transferred */ + ret = sd_read_rx_len(priv, &buf_len); + if (ret < 0) { + i++; + PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i); + if (i >= MAX_CMD52_RETRY) { + ret = BT_STATUS_FAILURE; + goto exit; + } + udelay(20); + } + } + while (ret == BT_STATUS_FAILURE); + + /* Allocate buffer */ + blksz = SD_BLOCK_SIZE; + buf_block_len = (buf_len + blksz - 1) / blksz; + if (buf_len <= BT_HEADER_LEN || + (buf_block_len * blksz) > ALLOC_BUF_SIZE) { + PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n", + buf_len); + ret = BT_STATUS_FAILURE; + goto exit; + } + skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + goto exit; + } + if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) { + skb_put(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + skb_pull(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + } + + payload = skb->data; + i = 0; + do { + ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: card_to_host, read iomem (%d) failed: %d\n", + i, ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) { + kfree_skb(skb); + skb = NULL; + goto exit; + } + } + } while (ret == BT_STATUS_FAILURE); + /* This is SDIO specific header length: byte[2][1][0], * type: byte[3] + (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ + buf_len = payload[0]; + buf_len |= (u16) payload[1] << 8; + type = payload[3]; + PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", mbt_dev->name, + buf_len, type); + if (buf_len > buf_block_len * blksz) { + PRINTM(ERROR, + "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n", + buf_len, buf_block_len * blksz); + ret = BT_STATUS_FAILURE; + kfree_skb(skb); + skb = NULL; + goto exit; + } + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len); + switch (type) { + case HCI_ACLDATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (*(u16 *) skb->data == 0xffff) { + skb_queue_tail(&priv->adapter->fwdump_queue, skb); + break; + } + bt_recv_frame(priv, skb); + break; + case HCI_SCODATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + bt_recv_frame(priv, skb); + break; + case HCI_EVENT_PKT: + /** add EVT Demux */ + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb)) + break; + switch (skb->data[0]) { + case 0x0E: + /** cmd complete */ + if (priv->debug_device_pending) { + if (priv->debug_ocf_ogf[0] == skb->data[3] && + priv->debug_ocf_ogf[1] == skb->data[4]) { + priv->debug_device_pending = 0; + priv->debug_ocf_ogf[0] = 0; + priv->debug_ocf_ogf[1] = 0; + /** debug cmd complete */ + if (debug_dev) { + skb->dev = (void *)mdev_debug; + mdev_recv_frame(skb); + mdev_debug->stat.byte_rx += + buf_len; + } + break; + } + } + if (skb->data[3] == 0x81 && skb->data[4] == 0xFE) { + /** NFC cmd complete */ + if (nfc_dev) { + skb->dev = (void *)mdev_nfc; + mdev_recv_frame(skb); + mdev_nfc->stat.byte_rx += buf_len; + } + break; + } + bt_recv_frame(priv, skb); + break; + case 0x0F: + /** cmd status */ + if (skb->data[4] == 0x81 && skb->data[5] == 0xFE) { + /** NFC cmd ststus */ + if (nfc_dev) { + skb->dev = (void *)mdev_nfc; + mdev_recv_frame(skb); + mdev_nfc->stat.byte_rx += buf_len; + } + break; + } + bt_recv_frame(priv, skb); + break; + case 0xFF: + /** Vendor specific pkt */ + if (skb->data[2] == 0xC0) { + /** NFC EVT */ + if (nfc_dev) { + skb->dev = (void *)mdev_nfc; + mdev_recv_frame(skb); + mdev_nfc->stat.byte_rx += buf_len; + } + break; + } + bt_recv_frame(priv, skb); + break; + default: + bt_recv_frame(priv, skb); + break; + } + break; + case MRVL_VENDOR_PKT: + /* Just think here need to back compatible FM */ + bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) + bt_recv_frame(priv, skb); + break; + default: + /* Driver specified event and command resp should be handle + here */ + PRINTM(INFO, "BT: Unknown PKT type:%d\n", type); + kfree_skb(skb); + skb = NULL; + break; + } +exit: + if (ret) { + if (mbt_dev) + mdev_bt->stat.err_rx++; + PRINTM(ERROR, "error when recv pkt!\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function removes the card + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_remove_card(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + card = sdio_get_drvdata(func); + if (card) { + bt_remove_card(card->priv); + kfree(card); + } + } + + LEAVE(); +} + +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_interrupt(struct sdio_func *func) +{ + bt_private *priv; + struct m_dev *m_dev = NULL; + struct sdio_mmc_card *card; + int ret = BT_STATUS_SUCCESS; + u8 ireg = 0; + u8 host_intstatus_reg = 0; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->priv) { + PRINTM(INFO, + "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", + __func__, func, card); + LEAVE(); + return; + } + priv = card->priv; + host_intstatus_reg = priv->psdio_device->reg->host_intstatus; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + if (priv->card_type == CARD_TYPE_SD8887 || + priv->card_type == CARD_TYPE_SD8897 || + priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987) { + ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0, + SD_BLOCK_SIZE); + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n", + ret); + goto done; + } + ireg = priv->adapter->hw_regs[host_intstatus_reg]; + } else { + ireg = sdio_readb(card->func, host_intstatus_reg, &ret); + } + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n", + ret); + goto done; + } + if (ireg != 0) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * Clear the interrupt status register and re-enable + * the interrupt + */ + PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name, + ireg); + priv->adapter->irq_recv = ireg; + if (priv->card_type == CARD_TYPE_SD8777 || + priv->card_type == CARD_TYPE_SD8787) { + sdio_writeb(card->func, + ~(ireg) & (DN_LD_HOST_INT_STATUS | + UP_LD_HOST_INT_STATUS), + host_intstatus_reg, &ret); + if (ret) { + PRINTM(ERROR, + "BT: sdio_write_ioreg: clear int status register failed\n"); + goto done; + } + } + } else { + PRINTM(ERROR, "BT: ERR: ireg=0\n"); + } + OS_INT_DISABLE; + priv->adapter->sd_ireg |= ireg; + OS_INT_RESTORE; + bt_interrupt(m_dev); +done: + LEAVE(); +} + +/** + * @brief This function checks if the interface is ready to download + * or not while other download interfaces are present + * + * @param priv A pointer to bt_private structure + * @param val Winner status (0: winner) + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_check_winner_status(bt_private *priv, u8 *val) +{ + + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0; + + ENTER(); + winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret); + if (ret != BT_STATUS_SUCCESS) { + LEAVE(); + return BT_STATUS_FAILURE; + } + *val = winner; + + LEAVE(); + return ret; +} + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** @brief This function tells lower driver that BT is suspended + * + * @param priv A pointer to bt_private structure + * @return None + */ +void +bt_is_suspended(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + priv->adapter->is_suspended = TRUE; + sdio_func_suspended(card->func); +} +#endif + +/** @brief This function handles client driver suspend + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +bt_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name); + mbt_hci_suspend_dev(m_dev); + skb_queue_purge(&priv->adapter->tx_queue); + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, "BT: HS not actived, suspend fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to suspend!\n"); + } + } + } + + priv->adapter->is_suspended = TRUE; + + LEAVE(); + /* We will keep the power when hs enabled successfully */ + if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) { +#ifdef MMC_PM_SKIP_RESUME_PROBE + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and " + "MMC_PM_SKIP_RESUME_PROBE\n"); + return sdio_set_host_pm_flags(func, + MMC_PM_KEEP_POWER | + MMC_PM_SKIP_RESUME_PROBE); +#else + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n"); + return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +#endif + } else { + PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n"); + return BT_STATUS_SUCCESS; + } +} + +void +bt_sdio_shutdown(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is shutdown\n", + sdio_func_id(func)); + return; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return; + } + + priv = cardp->priv; + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, "BT: HS not actived, shutdown fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to shutdown!\n"); + } + } + } + LEAVE(); +} + +/** @brief This function handles client driver resume + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS + */ +int +bt_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + + ENTER(); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + priv->adapter->is_suspended = FALSE; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name); +#ifdef BLE_WAKEUP + if (priv->ble_wakeup_buf) { + PRINTM(CMD, "BT: Send system resume event\n"); + bt_send_system_event(priv, FALSE); + } +#endif + mbt_hci_resume_dev(m_dev); + sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name); + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif +#endif + +/******************************************************** + Global Functions +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +static const struct dev_pm_ops bt_sdio_pm_ops = { + .suspend = bt_sdio_suspend, + .resume = bt_sdio_resume, +}; +#endif +#endif +static struct sdio_driver sdio_bt = { + .name = "sdio_bt", + .id_table = bt_ids, + .probe = sd_probe_card, + .remove = sd_remove_card, +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &bt_sdio_pm_ops, + .shutdown = bt_sdio_shutdown, + } +#endif +#endif +}; + +/** + * @brief This function registers the bt module in bus driver. + * + * @return An int pointer that keeps returned value + */ +int * +sbi_register(void) +{ + int *ret; + + ENTER(); + + if (sdio_register_driver(&sdio_bt) != 0) { + PRINTM(FATAL, "BT: SD Driver Registration Failed\n"); + LEAVE(); + return NULL; + } else + ret = (int *)1; + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the bt module in bus driver. + * + * @return N/A + */ +void +sbi_unregister(void) +{ + ENTER(); + sdio_unregister_driver(&sdio_bt); + LEAVE(); +} + +/** + * @brief This function registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_dev(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + u8 chiprev; + struct sdio_mmc_card *card = priv->bt_dev.card; + struct sdio_func *func; + u8 host_intstatus_reg = priv->psdio_device->reg->host_intstatus; + u8 host_int_rsr_reg = priv->psdio_device->reg->host_int_rsr_reg; + u8 card_misc_cfg_reg = priv->psdio_device->reg->card_misc_cfg_reg; + u8 card_revision_reg = priv->psdio_device->reg->card_revision; + u8 io_port_0_reg = priv->psdio_device->reg->io_port_0; + u8 io_port_1_reg = priv->psdio_device->reg->io_port_1; + u8 io_port_2_reg = priv->psdio_device->reg->io_port_2; + u8 card_magic_reg = CARD_MAGIC_REG; + u8 magic_val = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: Error: card or function is NULL!\n"); + goto failed; + } + func = card->func; + priv->hotplug_device = &func->dev; + + /* Initialize the private structure */ + strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name)); + priv->bt_dev.ioport = 0; + priv->bt_dev.fn = func->num; + + sdio_claim_host(func); + ret = sdio_claim_irq(func, sd_interrupt); + if (ret) { + PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret); + goto release_host; + } + ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); + if (ret) { + PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__); + goto release_irq; + } + + /* read Revision Register to get the chip revision number */ + chiprev = sdio_readb(func, card_revision_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n"); + goto release_irq; + } + priv->adapter->chip_rev = chiprev; + PRINTM(INFO, "revision=%#x\n", chiprev); + + magic_val = sdio_readb(func, card_magic_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n"); + goto release_irq; + } + priv->adapter->magic_val = magic_val; + PRINTM(INFO, "magic_val=%#x\n", magic_val); + + /* + * Read the HOST_INTSTATUS_REG for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + reg = sdio_readb(func, host_intstatus_reg, &ret); + if (ret < 0) + goto release_irq; + + /* Read the IO port */ + reg = sdio_readb(func, io_port_0_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= reg; + + reg = sdio_readb(func, io_port_1_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 8); + + reg = sdio_readb(func, io_port_2_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 16); + + PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn, + priv->bt_dev.ioport); + + if (priv->card_type == CARD_TYPE_SD8977) { + if (bt_intmode == INT_MODE_GPIO) { + PRINTM(MSG, "Enable GPIO-1 INT\n"); + sdio_writeb(func, ENABLE_GPIO_1_INT_MODE, + SCRATCH_REG_32, &ret); + if (ret < 0) + goto release_irq; + } + } + +#define SDIO_INT_MASK 0x3F + if (priv->card_type == CARD_TYPE_SD8887 || + priv->card_type == CARD_TYPE_SD8897 || + priv->card_type == CARD_TYPE_SD8797 || + priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987) { + /* Set Host interrupt reset to read to clear */ + reg = sdio_readb(func, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + /* Set auto re-enable */ + reg = sdio_readb(func, card_misc_cfg_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg, + &ret); + if (ret < 0) + goto release_irq; + } + + sdio_set_drvdata(func, card); + sdio_release_host(func); + + LEAVE(); + return BT_STATUS_SUCCESS; +release_irq: + sdio_release_irq(func); +release_host: + sdio_release_host(func); +failed: + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function de-registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_unregister_dev(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + if (card && card->func) { + sdio_claim_host(card->func); + sdio_release_irq(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + sdio_set_drvdata(card->func, NULL); + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_enable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sd_enable_host_int_mask(priv, HIM_ENABLE); + sd_get_rx_unit(priv); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_disable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sbi_disable_host_int_mask(priv, HIM_DISABLE); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param priv A pointer to bt_private structure + * @param payload A pointer to the data/cmd buffer + * @param nb Length of data/cmd + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + int ret = BT_STATUS_SUCCESS; + int buf_block_len; + int blksz; + int i = 0; + u8 *buf = NULL; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + buf = payload; + + blksz = SD_BLOCK_SIZE; + buf_block_len = (nb + blksz - 1) / blksz; + /* Allocate buffer and copy payload */ + if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) { + if (nb > MAX_TX_BUF_SIZE) { + PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb); + LEAVE(); + return BT_STATUS_FAILURE; + } + /* Ensure 8-byte aligned CMD buffer */ + buf = priv->adapter->tx_buf; + memcpy(buf, payload, nb); + } + sdio_claim_host(card->func); + do { + /* Transfer data to card */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: host_to_card, write iomem (%d) failed: %d\n", + i, ret); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) + goto exit; + } else { + PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n", + m_dev->name, nb); + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb); + } + } while (ret == BT_STATUS_FAILURE); + priv->bt_dev.tx_dnld_rdy = FALSE; +exit: + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function downloads firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_download_fw(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + sdio_claim_host(card->func); + if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) { + PRINTM(MSG, "BT: FW already downloaded!\n"); + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + /* Check if other interface is downloading */ + ret = sd_check_winner_status(priv, &winner); + if (ret == BT_STATUS_FAILURE) { + PRINTM(FATAL, "BT read winner status failed!\n"); + goto done; + } + if (winner) { + PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n", + winner); + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) { + PRINTM(FATAL, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + + do_gettimeofday(&priv->req_fw_time); + /* Download the main firmware via the helper firmware */ + if (sd_download_firmware_w_helper(priv)) { + PRINTM(INFO, "BT: FW download failed!\n"); + ret = BT_STATUS_FAILURE; + } + goto exit; +done: + sdio_release_host(card->func); +exit: + LEAVE(); + return ret; +err_register: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the interrupt status and handle it accordingly. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_get_int_status(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 sdio_ireg = 0; + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + OS_INT_DISABLE; + sdio_ireg = priv->adapter->sd_ireg; + priv->adapter->sd_ireg = 0; + OS_INT_RESTORE; + sdio_claim_host(card->func); + priv->adapter->irq_done = sdio_ireg; + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ + if (priv->bt_dev.tx_dnld_rdy) { /* tx_done already received */ + PRINTM(INFO, + "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", + priv->bt_dev.tx_dnld_rdy, sdio_ireg); + } else { + priv->bt_dev.tx_dnld_rdy = TRUE; + } + } + if (sdio_ireg & UP_LD_HOST_INT_STATUS) + sd_card_to_host(priv); + + ret = BT_STATUS_SUCCESS; + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function wakeup firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_wakeup_firmware(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + sdio_release_host(card->func); + PRINTM(CMD, "BT wake up firmware\n"); + + LEAVE(); + return ret; +} + +/** @brief This function updates the SDIO card types + * + * @param priv A Pointer to the bt_private structure + * @param card A Pointer to card + * + * @return N/A + */ +void +sdio_update_card_type(bt_private *priv, void *card) +{ + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card; + + /* Update card type */ + if (cardp->func->device == SD_DEVICE_ID_8777_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8777_BT_FN3) + priv->card_type = CARD_TYPE_SD8777; + else if (cardp->func->device == SD_DEVICE_ID_8787_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8787_BT_FN3) + priv->card_type = CARD_TYPE_SD8787; + else if (cardp->func->device == SD_DEVICE_ID_8887_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8887_BT_FN3) + priv->card_type = CARD_TYPE_SD8887; + else if (cardp->func->device == SD_DEVICE_ID_8897_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8897_BT_FN3) + priv->card_type = CARD_TYPE_SD8897; + else if (cardp->func->device == SD_DEVICE_ID_8797_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8797_BT_FN3) + priv->card_type = CARD_TYPE_SD8797; + else if (cardp->func->device == SD_DEVICE_ID_8977_BT_FN2) + priv->card_type = CARD_TYPE_SD8977; + else if (cardp->func->device == SD_DEVICE_ID_8997_BT_FN2) + priv->card_type = CARD_TYPE_SD8997; + else if (cardp->func->device == SD_DEVICE_ID_8987_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8987_BT_FN3) + priv->card_type = CARD_TYPE_SD8987; +} + +/** + * @brief This function get sdio device from card type + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +sdio_get_sdio_device(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 card_type = priv->card_type; + + ENTER(); + + switch (card_type) { + case CARD_TYPE_SD8777: + priv->psdio_device = &bt_sdio_sd8777; + break; + case CARD_TYPE_SD8787: + priv->psdio_device = &bt_sdio_sd8787; + break; + case CARD_TYPE_SD8887: + priv->psdio_device = &bt_sdio_sd8887; + break; + case CARD_TYPE_SD8897: + priv->psdio_device = &bt_sdio_sd8897; + break; + case CARD_TYPE_SD8797: + priv->psdio_device = &bt_sdio_sd8797; + break; + case CARD_TYPE_SD8977: + priv->psdio_device = &bt_sdio_sd8977; + break; + case CARD_TYPE_SD8997: + priv->psdio_device = &bt_sdio_sd8997; + break; + case CARD_TYPE_SD8987: + priv->psdio_device = &bt_sdio_sd8987; + break; + default: + PRINTM(ERROR, "BT can't get right card type \n"); + ret = BT_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +#define SD8897_INIT_START_REG 0xDC +#define SD8897_INIT_END_REG 0xE1 +#define SD8887_INIT_START_REG 0xA0 +#define SD8887_INIT_END_REG 0xA5 +#define SD8977_SD8997_INIT_START_REG 0xF1 +#define SD8977_SD8997_INIT_END_REG 0xF6 + +/** @brief This function dump the SDIO register + * + * @param priv A Pointer to the bt_private structure + * + * @return N/A + */ +void +bt_dump_sdio_regs(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + char buf[256], *ptr; + u8 loop, func, data; + unsigned int reg, reg_start, reg_end; + u8 index = 0; + unsigned int reg_table_8887[] = { 0x58, 0x59, 0x5c, 0x60, 0x64, 0x70, + 0x71, 0x72, 0x73, 0xd8, 0xd9, 0xda + }; + u8 loop_num = 0; + unsigned int *reg_table = NULL; + u8 reg_table_size = 0; + unsigned int init_reg_start = 0; + unsigned int init_reg_end = 0; + if (priv->card_type == CARD_TYPE_SD8887) { + init_reg_start = SD8887_INIT_START_REG; + init_reg_end = SD8887_INIT_END_REG; + } else if (priv->card_type == CARD_TYPE_SD8897) { + init_reg_start = SD8897_INIT_START_REG; + init_reg_end = SD8897_INIT_END_REG; + } else if (priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987) { + init_reg_start = SD8977_SD8997_INIT_START_REG; + init_reg_end = SD8977_SD8997_INIT_END_REG; + } + + if (priv->card_type == CARD_TYPE_SD8887) { + loop_num = 3; + reg_table = reg_table_8887; + reg_table_size = sizeof(reg_table_8887) / sizeof(int); + } else + loop_num = 2; + if (priv->adapter->ps_state) + sbi_wakeup_firmware(priv); + + sdio_claim_host(card->func); + for (loop = 0; loop < loop_num; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else if (loop == 2) { + /* Read specific registers of SDIO function1 */ + index = 0; + func = 2; + reg_start = reg_table[index++]; + reg_end = reg_table[reg_table_size - 1]; + } else { + func = 2; + reg_start = 0; + reg_end = 0x09; + } + if (loop == 2) + ptr += sprintf(ptr, "SDIO Func%d: ", func); + else + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, + reg_start, reg_end); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(card->func, reg, &ret); + else + data = sdio_readb(card->func, reg, &ret); + if (loop == 2) + ptr += sprintf(ptr, "(%#x)", reg); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + if (loop == 2 && reg < reg_end) + reg = reg_table[index++]; + else + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + + if (init_reg_start) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ", + init_reg_start, init_reg_end); + for (reg = init_reg_start; reg <= init_reg_end;) { + data = sdio_readb(card->func, reg, &ret); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + sdio_release_host(card->func); +} + +module_param(fw_name, charp, 0); +MODULE_PARM_DESC(fw_name, "Firmware name"); +module_param(bt_req_fw_nowait, int, 0); +MODULE_PARM_DESC(bt_req_fw_nowait, + "0: Use request_firmware API; 1: Use request_firmware_nowait API"); +module_param(multi_fn, int, 0); +MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;"); + +module_param(bt_intmode, int, 0); +MODULE_PARM_DESC(bt_intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO");
diff --git a/bt_sd8897/bt/hci_wrapper.h b/bt_sd8897/bt/hci_wrapper.h new file mode 100644 index 0000000..3537a17 --- /dev/null +++ b/bt_sd8897/bt/hci_wrapper.h
@@ -0,0 +1,180 @@ +/** @file hci_wrapper.h + * @brief This file contains HCI related definitions + * + * Copyright (C) 2011-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _HCI_WRAPPER_H_ +#define _HCI_WRAPPER_H_ + +#include <linux/module.h> +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +/** Define Seq num */ +#define BT_SEQ 0 +#define NFC_SEQ 2 +#define DEBUG_SEQ 3 + +/** Define dev type */ +#define BT_TYPE 1 +#define BT_AMP_TYPE 2 +#define NFC_TYPE 4 +#define DEBUG_TYPE 5 + +/** Define spec type */ +#define BLUEZ_SPEC 1 +#define IANYWHERE_SPEC 2 +#define GENERIC_SPEC 3 + +/** Define lock/unlock wrapper */ +#define mdev_req_lock(d) down(&d->req_lock) +#define mdev_req_unlock(d) up(&d->req_lock) + +/** Length of device name */ +#define DEV_NAME_LEN 32 + +/** Define struct m_dev */ +struct m_dev { + char name[DEV_NAME_LEN]; + int index; + unsigned long flags; + spinlock_t lock; + struct semaphore req_lock; + struct sk_buff_head rx_q; + wait_queue_head_t req_wait_q; + struct hci_dev_stats stat; + struct module *owner; + void *dev_pointer; + int dev_type; + int spec_type; + void *driver_data; + int read_continue_flag; + int wait_rx_complete; + int rx_complete_flag; + wait_queue_head_t rx_wait_q; + spinlock_t rxlock; + atomic_t extra_cnt; + + struct sk_buff *evt_skb; + struct sk_buff *acl_skb; + struct sk_buff *sco_skb; + + int (*open) (struct m_dev * m_dev); + int (*close) (struct m_dev * m_dev); + int (*flush) (struct m_dev * m_dev); + int (*send) (struct m_dev * m_dev, struct sk_buff * skb); + void (*destruct) (struct m_dev * m_dev); + void (*notify) (struct m_dev * m_dev, unsigned int evt); + int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg); + void (*query) (struct m_dev * m_dev, void *arg); + +}; + +/** Define struct mbt_dev */ +struct mbt_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; + __u8 type; + + __u16 pkt_type; + __u16 esco_type; + __u16 link_policy; + __u16 link_mode; + + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + + struct sk_buff *reassembly[3]; + + atomic_t promisc; +}; + +/** Define 'nfc' interface specific struct fm_dev */ +struct nfc_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; +}; + +struct debug_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; +}; + +/** This function frees m_dev allocation */ +void free_m_dev(struct m_dev *m_dev); + +/** + * @brief This function receives frames + * + * @param skb A pointer to struct sk_buff + * @return 0--success otherwise error code + */ +static inline int +mdev_recv_frame(struct sk_buff *skb) +{ + struct m_dev *m_dev = (struct m_dev *)skb->dev; + if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags) + && !test_bit(HCI_INIT, &m_dev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Incomming skb */ + bt_cb(skb)->incoming = 1; + + /* Time stamp */ + __net_timestamp(skb); + + /* Queue frame for rx task */ + skb_queue_tail(&m_dev->rx_q, skb); + + /* Wakeup rx thread */ + wake_up_interruptible(&m_dev->req_wait_q); + + return 0; +} + +/** + * @brief mbt dev suspend handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_suspend_dev(struct m_dev *m_dev) +{ + return 0; +} + +/** + * @brief mbt dev resume handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_resume_dev(struct m_dev *m_dev) +{ + return 0; +} + +#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8897/bt/mbt_char.c b/bt_sd8897/bt/mbt_char.c new file mode 100644 index 0000000..22f650a --- /dev/null +++ b/bt_sd8897/bt/mbt_char.c
@@ -0,0 +1,815 @@ +/** @file mbt_char.c + * + * @brief This file contains the char device function calls + * + * Copyright (C) 2010-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/path.h> +#include <linux/namei.h> +#include <linux/mount.h> + +#include "bt_drv.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include <linux/sched/signal.h> +#endif +#include "mbt_char.h" + +static LIST_HEAD(char_dev_list); + +static DEFINE_SPINLOCK(char_dev_list_lock); + +static int mbtchar_major = MBTCHAR_MAJOR_NUM; + +/** + * @brief Gets char device structure + * + * @param dev A pointer to char_dev + * + * @return kobject structure + */ +struct kobject * +chardev_get(struct char_dev *dev) +{ + struct kobject *kobj; + + kobj = bt_priv_get(dev->m_dev->driver_data); + if (!kobj) + return NULL; + PRINTM(INFO, "dev get kobj\n"); + kobj = kobject_get(&dev->kobj); + if (!kobj) + bt_priv_put(dev->m_dev->driver_data); + return kobj; +} + +/** + * @brief Prints char device structure + * + * @param dev A pointer to char_dev + * + * @return N/A + */ +void +chardev_put(struct char_dev *dev) +{ + if (dev) { + struct m_dev *m_dev = dev->m_dev; + PRINTM(INFO, "dev put kobj\n"); + kobject_put(&dev->kobj); + if (m_dev) + bt_priv_put(m_dev->driver_data); + } +} + +/** + * @brief Changes permissions of the dev + * + * @param name pointer to character + * @param mode mode_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chmod(char *name, mode_t mode) +{ + struct path path; + struct inode *inode; + struct iattr newattrs; + int ret; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chmod(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief Changes ownership of the dev + * + * @param name pointer to character + * @param user uid_t type data + * @param group gid_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chown(char *name, uid_t user, gid_t group) +{ + struct path path; + struct inode *inode = NULL; + struct iattr newattrs; + int ret = 0; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chown(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_valid = ATTR_CTIME; + if (user != (uid_t) (-1)) { + newattrs.ia_valid |= ATTR_UID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_uid = user; +#else + newattrs.ia_uid = KUIDT_INIT(user); +#endif + } + if (group != (gid_t) (-1)) { + newattrs.ia_valid |= ATTR_GID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_gid = group; +#else + newattrs.ia_gid = KGIDT_INIT(group); +#endif + } + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief write handler for char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes written + */ +ssize_t +chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos) +{ + int nwrite = 0; + struct sk_buff *skb; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + if (!test_bit(HCI_UP, &m_dev->flags)) { + LEAVE(); + return -EBUSY; + } + nwrite = count; + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n"); + LEAVE(); + return -ENOMEM; + } + + if (copy_from_user((void *)skb_put(skb, count), buf, count)) { + PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n"); + kfree_skb(skb); + nwrite = -EFAULT; + goto exit; + } + + skb->dev = (void *)m_dev; + bt_cb(skb)->pkt_type = *((unsigned char *)skb->data); + skb_pull(skb, 1); + + PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len); + + /* Send skb to the hci wrapper layer */ + if (m_dev->send(m_dev, skb)) { + PRINTM(ERROR, "Write: Fail\n"); + nwrite = 0; + /* Send failed */ + kfree_skb(skb); + } +exit: + LEAVE(); + return nwrite; +} + +/** + * @brief read handler for BT char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes read + */ +ssize_t +chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos) +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + /* Wait for rx data */ + add_wait_queue(&m_dev->req_wait_q, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + skb = skb_dequeue(&m_dev->rx_q); + if (skb) + break; + if (!test_bit(HCI_UP, &m_dev->flags)) { + ret = -EBUSY; + break; + } + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&m_dev->req_wait_q, &wait); + + if (!skb) + goto out; + + if (m_dev->read_continue_flag == 0) { + /* Put type byte before the data */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + } + DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len); + if (skb->len > count) { + /* user data length is smaller than the skb length */ + if (copy_to_user(buf, skb->data, count)) { + ret = -EFAULT; + goto outf; + } + skb_pull(skb, count); + skb_queue_head(&m_dev->rx_q, skb); + m_dev->read_continue_flag = 1; + wake_up_interruptible(&m_dev->req_wait_q); + ret = count; + goto out; + } else { + if (copy_to_user(buf, skb->data, skb->len)) { + ret = -EFAULT; + goto outf; + } + m_dev->read_continue_flag = 0; + ret = skb->len; + } +outf: + kfree_skb(skb); +out: + if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) { + m_dev->rx_complete_flag = TRUE; + wake_up_interruptible(&m_dev->rx_wait_q); + } + LEAVE(); + return ret; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl common handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg) +#else +/** + * @brief ioctl common handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +char_ioctl(struct file *filp, unsigned int cmd, void *arg) +#endif +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { + case MBTCHAR_IOCTL_RELEASE: + m_dev->close(m_dev); + break; + case MBTCHAR_IOCTL_QUERY_TYPE: + m_dev->query(m_dev, arg); + break; + default: + m_dev->ioctl(m_dev, cmd, arg); + break; + } + LEAVE(); + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, (void *)arg); +#else + return char_ioctl(filp, cmd, (void *)arg); +#endif +} + +#ifdef CONFIG_COMPAT +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief compat ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl_compat(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief compat ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, compat_ptr(arg)); +#else + return char_ioctl(filp, cmd, compat_ptr(arg)); +#endif +} +#endif /* CONFIG_COMPAT */ + +/** + * @brief open handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = NULL; + struct m_dev *m_dev = NULL; + struct char_dev *cdev = NULL; + struct list_head *p = NULL; + ENTER(); + + list_for_each(p, &char_dev_list) { + cdev = list_entry(p, struct char_dev, list); + if (mbtchar_major == MAJOR(inode->i_cdev->dev) && + cdev->minor == MINOR(inode->i_cdev->dev)) { + dev = cdev; + break; + } + } + if (!dev) { + PRINTM(ERROR, "cannot find dev from inode\n"); + LEAVE(); + return -ENXIO; + } + if (!chardev_get(dev)) { + LEAVE(); + return -ENXIO; + } + filp->private_data = dev; /* for other methods */ + m_dev = dev->m_dev; + mdev_req_lock(m_dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (test_bit(HCI_UP, &m_dev->flags)) { + atomic_inc(&m_dev->extra_cnt); + goto done; + } +#endif + if (m_dev->open(m_dev)) { + ret = -EIO; + goto done; + } + set_bit(HCI_UP, &m_dev->flags); + +done: + mdev_req_unlock(m_dev); + if (ret) + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief release handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) { + LEAVE(); + return ret; + } +#endif + if (m_dev) + ret = dev->m_dev->close(dev->m_dev); + filp->private_data = NULL; + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief poll handler for char dev + * + * @param filp pointer to structure file + * @param wait pointer to poll_table structure + * @return mask + */ +static unsigned int +chardev_poll(struct file *filp, poll_table * wait) +{ + unsigned int mask; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + + m_dev = dev->m_dev; + poll_wait(filp, &m_dev->req_wait_q, wait); + mask = POLLOUT | POLLWRNORM; + if (skb_peek(&m_dev->rx_q)) + mask |= POLLIN | POLLRDNORM; + if (!test_bit(HCI_UP, &(m_dev->flags))) + mask |= POLLHUP; + PRINTM(INFO, "poll mask=0x%x\n", mask); + LEAVE(); + return mask; +} + +/* File ops for the Char driver */ +const struct file_operations chardev_fops = { + .owner = THIS_MODULE, + .read = chardev_read, + .write = chardev_write, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + .ioctl = chardev_ioctl, +#else + .unlocked_ioctl = chardev_ioctl, +#endif +#ifdef CONFIG_COMPAT + .compat_ioctl = chardev_ioctl_compat, +#endif + .open = chardev_open, + .release = chardev_release, + .poll = chardev_poll, +}; + +/** + * @brief This function creates the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param mod_name A pointer to char + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name) +{ + int ret = 0, dev_num; + unsigned long flags; + ENTER(); + /* create the chrdev region */ + if (mbtchar_major) { + dev_num = MKDEV(mbtchar_major, dev->minor); + ret = register_chrdev_region(dev_num, 1, mod_name); + } else { + PRINTM(INFO, "chardev: no major # yet\n"); + ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1, + mod_name); + } + + if (ret) { + PRINTM(ERROR, "chardev: create chrdev_region failed\n"); + LEAVE(); + return ret; + } + if (!mbtchar_major) { + /* Store the allocated dev major # */ + mbtchar_major = MAJOR(dev_num); + } + dev->cdev = cdev_alloc(); + dev->cdev->ops = &chardev_fops; + dev->cdev->owner = chardev_fops.owner; + dev_num = MKDEV(mbtchar_major, dev->minor); + + if (cdev_add(dev->cdev, dev_num, 1)) { + PRINTM(ERROR, "chardev: cdev_add failed\n"); + ret = -EFAULT; + goto free_cdev_region; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } + if (dev->dev_type == NFC_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } + if (dev->dev_type == DEBUG_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } +#else + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } + if (dev->dev_type == NFC_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } + if (dev->dev_type == DEBUG_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } +#endif + PRINTM(INFO, "register char dev=%s\n", dev_name); + + /** modify later */ + + spin_lock_irqsave(&char_dev_list_lock, flags); + list_add_tail(&dev->list, &char_dev_list); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + + LEAVE(); + return ret; +free_cdev_region: + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + LEAVE(); + return ret; +} + +/** + * @brief This function deletes the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name) +{ + ENTER(); + device_destroy(char_class, MKDEV(mbtchar_major, dev->minor)); + cdev_del(dev->cdev); + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + PRINTM(INFO, "unregister char dev=%s\n", dev_name); + + LEAVE(); + return 0; +} + +/** + * @brief This function cleans module + * + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup(struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + do { + dev = NULL; + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + unregister_char_dev(dev, char_class, dev->m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } while (dev); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + class_destroy(char_class); + LEAVE(); +} + +/** + * @brief This function cleans module + * + * @param m_dev A pointer to m_dev struct + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + if (dev->minor == m_dev->index) { + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + dev->m_dev = NULL; + unregister_char_dev(dev, char_class, m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } + spin_unlock_irqrestore(&char_dev_list_lock, flags); + LEAVE(); +}
diff --git a/bt_sd8897/bt/mbt_char.h b/bt_sd8897/bt/mbt_char.h new file mode 100644 index 0000000..9997a38 --- /dev/null +++ b/bt_sd8897/bt/mbt_char.h
@@ -0,0 +1,72 @@ +/** @file mbt_char.h + * + * @brief This file contains mbtchar driver specific defines etc + * + * Copyright (C) 2010-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifndef __MBT_CHAR_H__ +#define __MBT_CHAR_H__ + +#include <linux/cdev.h> +#include <linux/device.h> + +/** Define ioctl */ +#define MBTCHAR_IOCTL_RELEASE _IO('M', 1) +#define MBTCHAR_IOCTL_QUERY_TYPE _IO('M', 2) +#ifdef BLE_WAKEUP +#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4) +#endif + +#define MBTCHAR_MAJOR_NUM (0) + +/** Interface specific macros */ +#define MBTCHAR_MINOR_BASE (0) +#define FMCHAR_MINOR_BASE (10) +#define NFCCHAR_MINOR_BASE (20) +#define DEBUGCHAR_MINOR_BASE (30) + +/** Declaration of char_dev struct */ +struct char_dev { + struct list_head list; + int minor; + int dev_type; + struct cdev *cdev; + struct m_dev *m_dev; + struct kobject kobj; +}; + +/** Changes permissions of the dev */ +int mbtchar_chmod(char *name, mode_t mode); + +/** Changes ownership of the dev */ +int mbtchar_chown(char *name, uid_t user, gid_t group); + +/** This function creates the char dev */ +int register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name); + +/** This function deletes the char dev */ +int unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name); + +/** This function cleans module */ +void chardev_cleanup(struct class *char_class); + +/** This function cleans module */ +void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class); + +#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8897/config/bt_cal_data.conf b/bt_sd8897/config/bt_cal_data.conf new file mode 100644 index 0000000..13aac4e --- /dev/null +++ b/bt_sd8897/config/bt_cal_data.conf
@@ -0,0 +1,5 @@ +00 1C 01 37 FF FF FF FF 02 04 7F 01 +CE BA 00 00 00 2D C6 C0 00 00 00 00 +00 F0 00 00 + +
diff --git a/bt_sd8897/config/bt_init_cfg.conf b/bt_sd8897/config/bt_init_cfg.conf new file mode 100644 index 0000000..deb5a63 --- /dev/null +++ b/bt_sd8897/config/bt_init_cfg.conf
@@ -0,0 +1,11 @@ +# File: init_cfg_.conf + +# MAC address (interface: address) +#ifdef BT_HCI_BLUEZ +mac_addr= hci0: 00:50:43:20:43:21 +#else +mac_addr= mbtchar0: 00:50:43:20:43:21 +#endif +# Register (type, offset, value) +bt_reg=2,0x00000026,0x0040 +
diff --git a/bt_sd8987/Makefile b/bt_sd8987/Makefile new file mode 100644 index 0000000..40037dd --- /dev/null +++ b/bt_sd8987/Makefile
@@ -0,0 +1,188 @@ +# File: Makefile +# Copyright (C) 2007-2018, Marvell International Ltd. +# + +CC= $(CROSS_COMPILE)clang +LD= $(CROSS_COMPILE)ld.lld + +BACKUP= /root/backup +YMD= `date +%Y%m%d%H%M` + +############################################################################# +# Configuration Options +############################################################################# + +# Debug Option +# DEBUG LEVEL n/1/2: +# n: NO DEBUG +# 1: PRINTM(MSG,...), PRINTM(FATAL,...), PRINTM(WARN,...) and PRINTM(INFO,...) +# 2: All PRINTM() +CONFIG_DEBUG=1 + + +# SDIO suspend/resume +CONFIG_SDIO_SUSPEND_RESUME=y + + +CONFIG_BLE_WAKEUP=y + +############################################################################# +# Select Platform Tools +############################################################################# + +MODEXT = ko + +ifeq ($(CONFIG_64BIT), y) + EXTRA_CFLAGS += -DMBT_64BIT +endif + +ifeq ($(CONFIG_T50), y) + EXTRA_CFLAGS += -DT50 + EXTRA_CFLAGS += -DT40 + EXTRA_CFLAGS += -DT3T +endif + +ifeq ($(CONFIG_BLE_WAKEUP), y) + EXTRA_CFLAGS += -DBLE_WAKEUP +endif + + + + + +KERNELDIR?=KBUILD_SRC + +EXTRA_CFLAGS += -I$(KERNELDIR)/include + +EXTRA_CFLAGS += -I$(M)/../mbtchar_src +EXTRA_CFLAGS += -I$(M)/bt +LD += -S + +#ifdef SD8xxx +BINDIR = ../bin_sd8xxx_btchar +#endif +BINDIR = ../bin_sd8987_btchar + + +############################################################################# +# Compiler Flags +############################################################################# + EXTRA_CFLAGS += -DFPNUM='"36"' + +ifeq ($(CONFIG_DEBUG),1) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 +endif + +ifeq ($(CONFIG_DEBUG),2) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 + EXTRA_CFLAGS += -DDEBUG_LEVEL2 + DBG= -dbg +endif + +ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y) + EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME +endif + +############################################################################# +# Make Targets +############################################################################# + +BT_CHAR_OBJS = bt_char/bt_main.o bt_char/bt_sdiommc.o bt_char/bt_proc.o bt_char/mbt_char.o +BT_CHAR_OBJS += bt_char/bt_init.o + +BT_BLOCK_OBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o +BT_BLOCK_OBJS += bt/bt_init.o + +ifneq ($(KERNELRELEASE),) + +ifneq ($(CONFIG_BERLIN_SDIO_BT_8987_CHAR_DRV),) + +BTOBJS = bt_char/bt_main.o bt_char/bt_sdiommc.o bt_char/bt_proc.o bt_char/mbt_char.o +BTOBJS += bt_char/bt_init.o +obj-$(CONFIG_BERLIN_SDIO_BT_8987_CHAR_DRV) := bt8xxx.o + +else + +BTOBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o +BTOBJS += bt/bt_init.o +obj-m := bt8xxx.o + +endif + +obj-$(MODULE_LINK) := bt8xxx.o +bt8xxx-objs := $(BTOBJS) + + + +# Otherwise we were called directly from the command line; invoke the kernel build system. +else +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules +endif + +############################################################### + +export CC LD EXTRA_CFLAGS KERNELDIR + +.PHONY: app/fm_app clean distclean + +app/fm_app: + $(MAKE) -C $@ + +echo: + +build: echo default + + @if [ ! -d $(BINDIR) ]; then \ + mkdir $(BINDIR); \ + fi + +ifeq ($(CONFIG_MULTI_INTERFACE), y) + cp -f mbt8xxx_sdio.$(MODEXT) $(BINDIR)/mbt8987_sdio$(DBG).$(MODEXT) +else + cp -f mbt8xxx.$(MODEXT) $(BINDIR)/mbt8987$(DBG).$(MODEXT) +endif + cp -r config $(BINDIR) + + + + + + cp -f README $(BINDIR) + + $(MAKE) -C app/fm_app $@ INSTALLDIR=$(BINDIR); + cp -f app/fm_app/fmapp $(BINDIR); + +clean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name "*.symvers" -exec rm {} \; + -find . -name "modules.order" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + $(MAKE) -C app/fm_app $@ + +install: default + +distclean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.orig" -exec rm {} \; + -find . -name "*.swp" -exec rm {} \; + -find . -name "*.*~" -exec rm {} \; + -find . -name "*~" -exec rm {} \; + -find . -name "*.d" -exec rm {} \; + -find . -name "*.a" -exec rm {} \; + -find . -name "tags" -exec rm {} \; + -find . -name ".*" -exec rm -rf 2> /dev/null \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + $(MAKE) -C app/fm_app $@ +# End of file; +
diff --git a/bt_sd8987/bt/bt_drv.h b/bt_sd8987/bt/bt_drv.h new file mode 100644 index 0000000..aa6fa19 --- /dev/null +++ b/bt_sd8987/bt/bt_drv.h
@@ -0,0 +1,885 @@ +/** @file bt_drv.h + * @brief This header file contains global constant/enum definitions, + * global variable declaration. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_DRV_H_ +#define _BT_DRV_H_ + +#include <linux/version.h> +#include <linux/kthread.h> +#include <linux/skbuff.h> +#include <linux/vmalloc.h> + +#include "hci_wrapper.h" + +/** MAX adapter BT driver supported */ +#define MAX_BT_ADAPTER 3 + +#ifndef BIT +/** BIT definition */ +#define BIT(x) (1UL << (x)) +#endif + +#ifdef MBT_64BIT +typedef u64 t_ptr; +#else +typedef u32 t_ptr; +#endif + +/** max number of adapter supported */ +#define MAX_BT_ADAPTER 3 +/** Define drv_mode bit */ +#define DRV_MODE_BT BIT(0) + +/** Define devFeature bit */ +#define DEV_FEATURE_BT BIT(0) +#define DEV_FEATURE_BTAMP BIT(1) +#define DEV_FEATURE_BLE BIT(2) + +/** Define maximum number of radio func supported */ +#define MAX_RADIO_FUNC 3 + +/** MAC address print format */ +#ifndef MACSTR +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +/** MAC address print arguments */ +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Debug level : Message */ +#define DBG_MSG BIT(0) +/** Debug level : Fatal */ +#define DBG_FATAL BIT(1) +/** Debug level : Error */ +#define DBG_ERROR BIT(2) +/** Debug level : Data */ +#define DBG_DATA BIT(3) +/** Debug level : Command */ +#define DBG_CMD BIT(4) +/** Debug level : Event */ +#define DBG_EVENT BIT(5) +/** Debug level : Interrupt */ +#define DBG_INTR BIT(6) + +/** Debug entry : Data dump */ +#define DBG_DAT_D BIT(16) +/** Debug entry : Data dump */ +#define DBG_CMD_D BIT(17) + +/** Debug level : Entry */ +#define DBG_ENTRY BIT(28) +/** Debug level : Warning */ +#define DBG_WARN BIT(29) +/** Debug level : Informative */ +#define DBG_INFO BIT(30) + +#ifdef DEBUG_LEVEL1 +extern u32 mbt_drvdbg; + +#ifdef DEBUG_LEVEL2 +/** Print informative message */ +#define PRINTM_INFO(msg...) \ + do {if (mbt_drvdbg & DBG_INFO) \ + printk(KERN_DEBUG msg); } while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) \ + do {if (mbt_drvdbg & DBG_WARN) \ + printk(KERN_DEBUG msg); } while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) \ + do {if (mbt_drvdbg & DBG_ENTRY) \ + printk(KERN_DEBUG msg); } while (0) +#else +/** Print informative message */ +#define PRINTM_INFO(msg...) do {} while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) do {} while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +/** Print interrupt message */ +#define PRINTM_INTR(msg...) \ + do {if (mbt_drvdbg & DBG_INTR) \ + printk(KERN_DEBUG msg); } while (0) +/** Print event message */ +#define PRINTM_EVENT(msg...) \ + do {if (mbt_drvdbg & DBG_EVENT) \ + printk(KERN_DEBUG msg); } while (0) +/** Print command message */ +#define PRINTM_CMD(msg...) \ + do {if (mbt_drvdbg & DBG_CMD) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data message */ +#define PRINTM_DATA(msg...) \ + do {if (mbt_drvdbg & DBG_DATA) \ + printk(KERN_DEBUG msg); } while (0) +/** Print error message */ +#define PRINTM_ERROR(msg...) \ + do {if (mbt_drvdbg & DBG_ERROR) \ + printk(KERN_ERR msg); } while (0) +/** Print fatal message */ +#define PRINTM_FATAL(msg...) \ + do {if (mbt_drvdbg & DBG_FATAL) \ + printk(KERN_ERR msg); } while (0) +/** Print message */ +#define PRINTM_MSG(msg...) \ + do {if (mbt_drvdbg & DBG_MSG) \ + printk(KERN_ALERT msg); } while (0) + +/** Print data dump message */ +#define PRINTM_DAT_D(msg...) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data dump message */ +#define PRINTM_CMD_D(msg...) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + printk(KERN_DEBUG msg); } while (0) + +/** Print message with required level */ +#define PRINTM(level, msg...) PRINTM_##level(msg) + +/** Debug dump buffer length */ +#define DBG_DUMP_BUF_LEN 64 +/** Maximum number of dump per line */ +#define MAX_DUMP_PER_LINE 16 +/** Maximum data dump length */ +#define MAX_DATA_DUMP_LEN 48 + +/** + * @brief Prints buffer data upto provided length + * + * @param prompt Char pointer + * @param buf Buffer + * @param len Length + * + * @return N/A + */ +static inline void +hexdump(char *prompt, u8 *buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + printk(KERN_DEBUG "%s: len=%d\n", prompt, len); + for (i = 1; i <= len; i++) { + ptr += snprintf(ptr, 4, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +/** Debug hexdump of debug data */ +#define DBG_HEXDUMP_DAT_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + hexdump(x, y, z); } while (0) +/** Debug hexdump of debug command */ +#define DBG_HEXDUMP_CMD_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + hexdump(x, y, z); } while (0) + +/** Debug hexdump */ +#define DBG_HEXDUMP(level, x, y, z) DBG_HEXDUMP_##level(x, y, z) + +/** Mark entry point */ +#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +/** Mark exit point */ +#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +#else +/** Do nothing */ +#define PRINTM(level, msg...) do {} while (0) +/** Do nothing */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) +/** Do nothing */ +#define ENTER() do {} while (0) +/** Do nothing */ +#define LEAVE() do {} while (0) +#endif /* DEBUG_LEVEL1 */ + +/** Bluetooth upload size */ +#define BT_UPLD_SIZE 2312 +/** Bluetooth status success */ +#define BT_STATUS_SUCCESS (0) +/** Bluetooth status pending */ +#define BT_STATUS_PENDING (1) +/** Bluetooth status failure */ +#define BT_STATUS_FAILURE (-1) + +#ifndef TRUE +/** True value */ +#define TRUE 1 +#endif +#ifndef FALSE +/** False value */ +#define FALSE 0 +#endif + +/** Set thread state */ +#define OS_SET_THREAD_STATE(x) set_current_state(x) +/** Time to wait until Host Sleep state change in millisecond */ +#define WAIT_UNTIL_HS_STATE_CHANGED 2000 +/** Time to wait cmd resp in millisecond */ +#define WAIT_UNTIL_CMD_RESP 5000 + +/** Sleep until a condition gets true or a timeout elapses */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000)) +#else +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) +#endif + +#define os_wait_timeout(waitq, cond, timeout) \ + wait_event_timeout(waitq, cond, ((timeout) * HZ / 1000)) + +/** bt thread structure */ +typedef struct { + /** Task */ + struct task_struct *task; + /** Queue */ + wait_queue_head_t waitQ; + /** PID */ + pid_t pid; + /** Private structure */ + void *priv; +} bt_thread; + +/** + * @brief Activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_activate_thread(bt_thread *thr) +{ + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitQ); + + /** Record the thread pid */ + thr->pid = current->pid; +} + +/** + * @brief De-activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_deactivate_thread(bt_thread *thr) +{ + thr->pid = 0; + return; +} + +/** + * @brief Creates bt thread + * + * @param btfunc Function pointer + * @param thr A pointer to bt_thread structure + * @param name Char pointer + * + * @return N/A + */ +static inline void +bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name) +{ + thr->task = kthread_run(btfunc, thr, "%s", name); +} + +/** + * @brief Delete bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline int +bt_terminate_thread(bt_thread *thr) +{ + /* Check if the thread is active or not */ + if (!thr->pid) + return -1; + + kthread_stop(thr->task); + return 0; +} + +/** + * @brief Set scheduled timeout + * + * @param millisec Time unit in ms + * + * @return N/A + */ +static inline void +os_sched_timeout(u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + + schedule_timeout((millisec * HZ) / 1000); +} + +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__((packed)) +#endif + +/** Data structure for the Marvell Bluetooth device */ +typedef struct _bt_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** m_dev structure */ + struct m_dev m_dev[MAX_RADIO_FUNC]; + + /** Tx download ready flag */ + u8 tx_dnld_rdy; + /** Function */ + u8 fn; + /** Rx unit */ + u8 rx_unit; + /** Power Save mode : Timeout configuration */ + u16 idle_timeout; + /** Power Save mode */ + u8 psmode; + /** Power Save command */ + u8 pscmd; + /** Host Sleep mode */ + u8 hsmode; + /** Host Sleep command */ + u8 hscmd; + /** Low byte is gap, high byte is GPIO */ + u16 gpio_gap; + /** Host Sleep configuration command */ + u8 hscfgcmd; + /** Host Send Cmd Flag */ + u8 sendcmdflag; + /** opcode for Send Cmd */ + u16 send_cmd_opcode; + /** Device Type */ + u8 devType; + /** Device Features */ + u8 devFeature; + /** cmd52 function */ + u8 cmd52_func; + /** cmd52 register */ + u8 cmd52_reg; + /** cmd52 value */ + u8 cmd52_val; + /** SDIO pull control command */ + u8 sdio_pull_ctrl; + /** Low 2 bytes is pullUp, high 2 bytes for pull-down */ + u32 sdio_pull_cfg; + /** Test mode command */ + u8 test_mode; +} bt_dev_t, *pbt_dev_t; + +/** Marvell bt adapter structure */ +typedef struct _bt_adapter { + /** Chip revision ID */ + u8 chip_rev; + /** magic val */ + u8 magic_val; + /** Surprise removed flag */ + u8 SurpriseRemoved; + /** IRQ number */ + int irq; + /** Interrupt counter */ + u32 IntCounter; + /** Tx packet queue */ + struct sk_buff_head tx_queue; + + /** Pointer of fw dump file name */ + char *fwdump_fname; + /** Pending Tx packet queue */ + struct sk_buff_head pending_queue; + /** tx lock flag */ + u8 tx_lock; + /** Power Save mode */ + u8 psmode; + /** Power Save state */ + u8 ps_state; + /** Host Sleep state */ + u8 hs_state; + /** hs skip count */ + u32 hs_skip; + /** suspend_fail flag */ + u8 suspend_fail; + /** suspended flag */ + u8 is_suspended; + /** Number of wakeup tries */ + u8 WakeupTries; + /** Host Sleep wait queue */ + wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__; + /** Host Cmd complet state */ + u8 cmd_complete; + /** indicate using wait event timeout */ + u8 wait_event_timeout; + /** last irq recv */ + u8 irq_recv; + /** last irq processed */ + u8 irq_done; + /** sdio int status */ + u8 sd_ireg; + /** buf allocated for transmit */ + u8 *tx_buffer; + /** buf for transmit */ + u8 *tx_buf; + /** buf allocated for read interrupt status */ + u8 *hw_regs_buf; + /** buf for read interrupt status */ + u8 *hw_regs; + /** tx pending */ + u32 skb_pending; +/** Version string buffer length */ +#define MAX_VER_STR_LEN 128 + /** Driver version */ + u8 drv_ver[MAX_VER_STR_LEN]; + /** Number of command timeout */ + u32 num_cmd_timeout; +} bt_adapter, *pbt_adapter; + +/** Length of prov name */ +#define PROC_NAME_LEN 32 + +/** Item data structure */ +struct item_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** Size */ + u32 size; + /** Address */ + t_ptr addr; + /** Offset */ + u32 offset; + /** Flag */ + u32 flag; +}; + +/** Proc private data structure */ +struct proc_private_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** File flag */ + u32 fileflag; + /** Buffer size */ + u32 bufsize; + /** Number of items */ + u32 num_items; + /** Item data */ + struct item_data *pdata; + /** Private structure */ + struct _bt_private *pbt; + /** File operations */ + const struct file_operations *fops; +}; + +/** Device proc structure */ +struct device_proc { + /** Proc directory entry */ + struct proc_dir_entry *proc_entry; + /** num of proc files */ + u8 num_proc_files; + /** pointer to proc_private_data */ + struct proc_private_data *pfiles; +}; + +/** Private structure for the MV device */ +typedef struct _bt_private { + /** Bluetooth device */ + bt_dev_t bt_dev; + /** Adapter */ + bt_adapter *adapter; + /** Firmware helper */ + const struct firmware *fw_helper; + /** Firmware */ + const struct firmware *firmware; + /** Init user configure file */ + const struct firmware *init_user_cfg; + /** Init user configure wait queue token */ + u16 init_user_conf_wait_flag; + /** Init user configure file wait queue */ + wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__; + /** Firmware request start time */ + struct timeval req_fw_time; + /** Hotplug device */ + struct device *hotplug_device; + /** thread to service interrupts */ + bt_thread MainThread; + /** proc data */ + struct device_proc dev_proc[MAX_RADIO_FUNC]; + /** Driver lock */ + spinlock_t driver_lock; + /** Driver lock flags */ + ulong driver_flags; + /** Driver reference flags */ + struct kobject kobj; + /** CRC check flag */ + u16 fw_crc_check; + /** Card type */ + u16 card_type; + /** sdio device */ + const struct sdio_device *psdio_device; + u8 fw_reload; + /** fw dump state */ + u8 fw_dump; +} bt_private, *pbt_private; + +/** Disable interrupt */ +#define OS_INT_DISABLE spin_lock_irqsave(&priv->driver_lock, \ + priv->driver_flags) +/** Enable interrupt */ +#define OS_INT_RESTORE spin_unlock_irqrestore(&priv->driver_lock, \ + priv->driver_flags) + +#ifndef HCI_BT_AMP +/** BT_AMP flag for device type */ +#define HCI_BT_AMP 0x80 +#endif + +/** Device type of BT */ +#define DEV_TYPE_BT 0x00 +/** Device type of AMP */ +#define DEV_TYPE_AMP 0x01 + +/** Marvell vendor packet */ +#define MRVL_VENDOR_PKT 0xFE + +/** Bluetooth command : Get FW Version */ +#define BT_CMD_GET_FW_VERSION 0x0F +/** Bluetooth command : Sleep mode */ +#define BT_CMD_AUTO_SLEEP_MODE 0x23 +/** Bluetooth command : Host Sleep configuration */ +#define BT_CMD_HOST_SLEEP_CONFIG 0x59 +/** Bluetooth command : Host Sleep enable */ +#define BT_CMD_HOST_SLEEP_ENABLE 0x5A +/** Bluetooth command : Module Configuration request */ +#define BT_CMD_MODULE_CFG_REQ 0x5B +/** Bluetooth command : PMIC Configure */ +#define BT_CMD_PMIC_CONFIGURE 0x7D + +/** Bluetooth command : SDIO pull up down configuration request */ +#define BT_CMD_SDIO_PULL_CFG_REQ 0x69 +/** Bluetooth command : Set Evt Filter Command */ +#define BT_CMD_SET_EVT_FILTER 0x05 +/** Bluetooth command : Enable Write Scan Command */ +#define BT_CMD_ENABLE_WRITE_SCAN 0x1A +/** Bluetooth command : Enable Device under test mode */ +#define BT_CMD_ENABLE_DEVICE_TESTMODE 0x03 +/** Sub Command: Module Bring Up Request */ +#define MODULE_BRINGUP_REQ 0xF1 +/** Sub Command: Module Shut Down Request */ +#define MODULE_SHUTDOWN_REQ 0xF2 +/** Module already up */ +#define MODULE_CFG_RESP_ALREADY_UP 0x0c +/** Sub Command: Host Interface Control Request */ +#define MODULE_INTERFACE_CTRL_REQ 0xF5 + +/** Bluetooth event : Power State */ +#define BT_EVENT_POWER_STATE 0x20 + +/** Bluetooth Power State : Enable */ +#define BT_PS_ENABLE 0x02 +/** Bluetooth Power State : Disable */ +#define BT_PS_DISABLE 0x03 +/** Bluetooth Power State : Sleep */ +#define BT_PS_SLEEP 0x01 +/** Bluetooth Power State : Awake */ +#define BT_PS_AWAKE 0x02 + +/** Vendor OGF */ +#define VENDOR_OGF 0x3F +/** OGF for reset */ +#define RESET_OGF 0x03 +/** Bluetooth command : Reset */ +#define BT_CMD_RESET 0x03 + +/** Host Sleep activated */ +#define HS_ACTIVATED 0x01 +/** Host Sleep deactivated */ +#define HS_DEACTIVATED 0x00 + +/** Power Save sleep */ +#define PS_SLEEP 0x01 +/** Power Save awake */ +#define PS_AWAKE 0x00 + +/** bt header length */ +#define BT_HEADER_LEN 4 + +#ifndef MAX +/** Return maximum of two */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/** This is for firmware specific length */ +#define EXTRA_LEN 36 + +/** Command buffer size for Marvell driver */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Bluetooth Rx packet buffer size for Marvell driver */ +#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \ + (HCI_MAX_FRAME_SIZE + EXTRA_LEN) + +/** Buffer size to allocate */ +#define ALLOC_BUF_SIZE (((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \ + MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \ + + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) + +/** Request FW timeout in second */ +#define REQUEST_FW_TIMEOUT 30 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** The number of times to try when waiting for downloaded firmware to + become active when multiple interface is present */ +#define MAX_MULTI_INTERFACE_POLL_TRIES 150 + +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** default idle time */ +#define DEFAULT_IDLE_TIME 1000 + +#define BT_CMD_HEADER_SIZE 3 + +#define BT_CMD_DATA_LEN 128 +#define BT_EVT_DATA_LEN 8 + +/** BT command structure */ +typedef struct _BT_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** Data */ + u8 data[BT_CMD_DATA_LEN]; +} __ATTRIB_PACK__ BT_CMD; + +/** BT event structure */ +typedef struct _BT_EVENT { + /** Event Counter */ + u8 EC; + /** Length */ + u8 length; + /** Data */ + u8 data[BT_EVT_DATA_LEN]; +} BT_EVENT; + +#if defined(SDIO_SUSPEND_RESUME) +#define DEF_GPIO_GAP 0xffff +#endif + +/** This function verify the received event pkt */ +int check_evtpkt(bt_private *priv, struct sk_buff *skb); + +/* Prototype of global function */ +/** This function gets the priv reference */ +struct kobject *bt_priv_get(bt_private *priv); +/** This function release the priv reference */ +void bt_priv_put(bt_private *priv); +/** This function adds the card */ +bt_private *bt_add_card(void *card); +/** This function removes the card */ +int bt_remove_card(void *card); +/** This function handles the interrupt */ +void bt_interrupt(struct m_dev *m_dev); + +/** This function creates proc interface directory structure */ +int bt_root_proc_init(void); +/** This function removes proc interface directory structure */ +int bt_root_proc_remove(void); +/** This function initializes proc entry */ +int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq); +/** This function removes proc interface */ +void bt_proc_remove(bt_private *priv); + +/** This function process the received event */ +int bt_process_event(bt_private *priv, struct sk_buff *skb); +/** This function enables host sleep */ +int bt_enable_hs(bt_private *priv, bool is_shutdown); +/** This function used to send command to firmware */ +int bt_prepare_command(bt_private *priv); +/** This function frees the structure of adapter */ +void bt_free_adapter(bt_private *priv); +/** This function handle the receive packet */ +void bt_recv_frame(bt_private *priv, struct sk_buff *skb); +void bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len); + +/** clean up m_devs */ +void clean_up_m_devs(bt_private *priv); +/** bt driver call this function to register to bus driver */ +int *sbi_register(void); +/** bt driver call this function to unregister to bus driver */ +void sbi_unregister(void); +/** bt driver calls this function to register the device */ +int sbi_register_dev(bt_private *priv); +/** bt driver calls this function to unregister the device */ +int sbi_unregister_dev(bt_private *priv); +/** This function initializes firmware */ +int sbi_download_fw(bt_private *priv); +/** Configures hardware to quit deep sleep state */ +int sbi_wakeup_firmware(bt_private *priv); +/** Module configuration and register device */ +int sbi_register_conf_dpc(bt_private *priv); + +/** This function is used to send the data/cmd to hardware */ +int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb); +/** This function reads the current interrupt status register */ +int sbi_get_int_status(bt_private *priv); +/** This function enables the host interrupts */ +int sbi_enable_host_int(bt_private *priv); +/** This function disables the host interrupts */ +int sbi_disable_host_int(bt_private *priv); + +/** bt fw reload flag */ +extern int bt_fw_reload; +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 +/** This function reload firmware */ +void bt_request_fw_reload(bt_private *priv, int mode); +#define MAX_TX_BUF_SIZE 2312 +/** This function downloads firmware image to the card */ +int sd_download_firmware_w_helper(bt_private *priv); +void bt_dump_sdio_regs(bt_private *priv); +#define FW_DUMP_TYPE_ENDED 0x002 +#define FW_DUMP_TYPE_MEM_ITCM 0x004 +#define FW_DUMP_TYPE_MEM_DTCM 0x005 +#define FW_DUMP_TYPE_MEM_SQRAM 0x006 +#define FW_DUMP_TYPE_MEM_IRAM 0x007 +#define FW_DUMP_TYPE_REG_MAC 0x009 +#define FW_DUMP_TYPE_REG_CIU 0x00E +#define FW_DUMP_TYPE_REG_APU 0x00F +#define FW_DUMP_TYPE_REG_ICU 0x014 +/* dumps the firmware to /var/ or /data/ */ +void bt_dump_firmware_info_v2(bt_private *priv); + +/** Max line length allowed in init config file */ +#define MAX_LINE_LEN 256 +/** Max MAC address string length allowed */ +#define MAX_MAC_ADDR_LEN 18 +/** Max register type/offset/value etc. parameter length allowed */ +#define MAX_PARAM_LEN 12 + +/** Bluetooth command : Mac address configuration */ +#define BT_CMD_CONFIG_MAC_ADDR 0x22 +/** Bluetooth command : Write CSU register */ +#define BT_CMD_CSU_WRITE_REG 0x66 +/** Bluetooth command : Load calibrate data */ +#define BT_CMD_LOAD_CONFIG_DATA 0x61 +/** Bluetooth command : Load calibrate ext data */ +#define BT_CMD_LOAD_CONFIG_DATA_EXT 0x60 + +/** Bluetooth command : BLE deepsleep */ +#define BT_CMD_BLE_DEEP_SLEEP 0x8b + +/** BT_BLE command structure */ +typedef struct _BT_BLE_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** deepsleep flag */ + u8 deepsleep; +} __ATTRIB_PACK__ BT_BLE_CMD; + +/** BT_CSU command structure */ +typedef struct _BT_CSU_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** reg type */ + u8 type; + /** address */ + u8 offset[4]; + /** Data */ + u8 value[2]; +} __ATTRIB_PACK__ BT_CSU_CMD; + +/** This function sets mac address */ +int bt_set_mac_address(bt_private *priv, u8 *mac); +/** This function writes value to CSU registers */ +int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value); +/** BT set user defined init data and param */ +int bt_init_config(bt_private *priv, char *cfg_file); +/** BT PMIC Configure command */ +int bt_pmic_configure(bt_private *priv); +/** This function load the calibrate data */ +int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac); +/** This function load the calibrate ext data */ +int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len); +/** BT set user defined calibration data */ +int bt_cal_config(bt_private *priv, char *cfg_file, char *mac); +/** BT set user defined calibration ext data */ +int bt_cal_config_ext(bt_private *priv, char *cfg_file); +int bt_init_mac_address(bt_private *priv, char *mac); + +int bt_set_independent_reset(bt_private *priv); +/** Bluetooth command : Independent reset */ +#define BT_CMD_INDEPENDENT_RESET 0x0D + +/** BT HCI command structure */ +typedef struct _BT_HCI_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** cmd type */ + u8 cmd_type; + /** cmd len */ + u8 cmd_len; + /** Data */ + u8 data[6]; +} __ATTRIB_PACK__ BT_HCI_CMD; + +#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8987/bt/bt_init.c b/bt_sd8987/bt/bt_init.c new file mode 100644 index 0000000..904b66f --- /dev/null +++ b/bt_sd8987/bt/bt_init.c
@@ -0,0 +1,752 @@ +/** @file bt_init.c + * + * @brief This file contains the init functions for BlueTooth + * driver. + * + * Copyright (C) 2011-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/string.h> +#include <linux/firmware.h> + +#include "bt_drv.h" + +extern int bt_req_fw_nowait; + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) + +#define isdigit(c) (('0' <= (c) && (c) <= '9')) +#define isspace(c) (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9))) +/** + * @brief Returns hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +static int +bt_hexval(char chr) +{ + ENTER(); + + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + LEAVE(); + return 0; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +static char * +bt_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Returns hex value of a given ascii string + * + * @param a String to be converted + * + * @return hex value + */ +static int +bt_atox(const char *a) +{ + int i = 0; + ENTER(); + while (isxdigit(*a)) + i = i * 16 + bt_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Converts mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +static void +bt_mac2u8(u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + memcpy(mac_buff, buf, strlen(buf)); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = bt_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = bt_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +/** + * @brief Returns integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_atoi(int *data, char *a) +{ + int i, val = 0, len; + + ENTER(); + + len = strlen(a); + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = bt_atox(a); + return BT_STATUS_SUCCESS; + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + PRINTM(ERROR, "Invalid char %c in string %s\n", a[i], + a); + return BT_STATUS_FAILURE; + } + } + *data = val; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief parse cal-data + * + * @param src a pointer to cal-data string + * @param len len of cal-data + * @param dst a pointer to return cal-data + * @param dst_size size of dest buffer + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size) +{ + const u8 *ptr; + u8 *dptr; + u32 count = 0; + int ret = BT_STATUS_FAILURE; + + ENTER(); + ptr = src; + dptr = dst; + + while ((ptr - src) < len) { + if (*ptr && isspace(*ptr)) { + ptr++; + continue; + } + + if (isxdigit(*ptr)) { + if ((dptr - dst) >= *dst_size) { + PRINTM(ERROR, "cal_file size too big!!!\n"); + goto done; + } + *dptr++ = bt_atox((const char *)ptr); + ptr += 2; + count++; + } else { + ptr++; + } + } + if (dptr == dst) { + ret = BT_STATUS_FAILURE; + goto done; + } + + *dst_size = count; + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief BT get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return -1 or length of the line + */ +int +parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos) +{ + static s32 pos; + u8 *src, *dest; + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while ((dest - line_pos < MAX_LINE_LEN - 1) && pos < size && + *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + *dest = '\0'; + /* parse new line */ + pos++; + return strlen((const char *)line_pos); +} + +/** + * @brief BT parse ASCII format data to MAC address + * + * @param priv BT private handle + * @param data Source data + * @param size data length + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_init_cfg(bt_private *priv, u8 *data, u32 size) +{ + u8 *pos; + u8 *intf_s, *intf_e; + u8 s[MAX_LINE_LEN]; /* 1 line data */ + u32 line_len; + char dev_name[MAX_PARAM_LEN]; + u8 buf[MAX_PARAM_LEN]; + u8 bt_addr[MAX_MAC_ADDR_LEN]; + u8 bt_mac[ETH_ALEN]; + int setting = 0; + u8 type = 0; + u16 value = 0; + u32 offset = 0; + int ret = BT_STATUS_FAILURE; + + memset(dev_name, 0, sizeof(dev_name)); + memset(bt_addr, 0, sizeof(bt_addr)); + memset(bt_mac, 0, sizeof(bt_mac)); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Need n't process this line */ + + /* Process MAC addr */ + if (strncmp((char *)pos, "mac_addr", 8) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(dev_name, (const char *)intf_s + 1, + intf_e - intf_s - 1); + dev_name[intf_e - intf_s - 1] = '\0'; + strncpy((char *)bt_addr, + (const char *)intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + /* Convert MAC format */ + bt_mac2u8(bt_mac, (char *)bt_addr); + PRINTM(CMD, + "HCI: %s new BT Address " MACSTR "\n", + dev_name, MAC2STR(bt_mac)); + if (BT_STATUS_SUCCESS != + bt_set_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp((char *)pos, "bt_reg", 6) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s + 1, + 1); + buf[1] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + type = (u8)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg type\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = (u8 *)strchr((const char *)intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, + intf_e - intf_s); + buf[intf_e - intf_s] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + offset = (u32)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg offset\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) { + PRINTM(ERROR, + "BT: Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, sizeof(buf)); + if (0 == bt_atoi(&setting, (char *)buf)) + value = (u16) setting; + else { + PRINTM(ERROR, "BT: Fail to parse reg value\n"); + goto done; + } + + PRINTM(CMD, + "BT: Write reg type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + if (BT_STATUS_SUCCESS != + bt_write_reg(priv, type, offset, value)) { + PRINTM(FATAL, + "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + goto done; + } + } + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to bt_private structure + * + * @return N/A + */ +static void +bt_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + bt_private *priv = (bt_private *)context; + + ENTER(); + + if (!firmware) + PRINTM(ERROR, "BT user init config request firmware failed\n"); + + priv->init_user_cfg = firmware; + priv->init_user_conf_wait_flag = TRUE; + wake_up_interruptible(&priv->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief BT set user defined init data and param + * + * @param priv BT private handle + * @param cfg_file user cofig file + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_config(bt_private *priv, char *cfg_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cfg) + ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + u8 cal_data[32]; + u8 *mac_data = NULL; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + u8 *pcal_data = cal_data; + + memset(bt_mac, 0, sizeof(bt_mac)); + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (mac != NULL) { + /* Convert MAC format */ + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: new BT Address " MACSTR "\n", + MAC2STR(bt_mac)); + mac_data = bt_mac; + } + if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size) +{ + u8 cal_data[128]; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (BT_STATUS_SUCCESS != + bt_load_cal_data_ext(priv, cal_data, cal_data_len)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config(bt_private *priv, char *cal_file, char *mac) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config_ext(bt_private *priv, char *cal_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT init mac address from bt_mac parametre when insmod + * + * @param priv a pointer to bt_private structure + * @param bt_mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_mac_address(bt_private *priv, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + int ret = BT_STATUS_FAILURE; + + ENTER(); + memset(bt_mac, 0, sizeof(bt_mac)); + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac)); + ret = bt_set_mac_address(priv, bt_mac); + if (ret != BT_STATUS_SUCCESS) + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre.\n"); + + LEAVE(); + return ret; +}
diff --git a/bt_sd8987/bt/bt_main.c b/bt_sd8987/bt/bt_main.c new file mode 100644 index 0000000..8086c0a --- /dev/null +++ b/bt_sd8987/bt/bt_main.c
@@ -0,0 +1,3652 @@ +/** @file bt_main.c + * + * @brief This file contains the major functions in BlueTooth + * driver. It includes init, exit, open, close and main + * thread etc.. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/** + * @mainpage M-BT Linux Driver + * + * @section overview_sec Overview + * + * The M-BT is a Linux reference driver for Marvell Bluetooth chipset. + * + * @section copyright_sec Copyright + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + */ +#include <linux/module.h> + +#ifdef CONFIG_OF +#include <linux/of.h> +#endif + +#include <linux/mmc/sdio_func.h> + +#include "bt_drv.h" +#include "mbt_char.h" +#include "bt_sdio.h" + +/** Version */ +#define VERSION "C4X14113" + +/** Driver version */ +static char mbt_driver_version[] = "SD8XXX-%s-" VERSION "-(" "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** SD8787 Card */ +#define CARD_SD8787 "SD8787" +/** SD8777 Card */ +#define CARD_SD8777 "SD8777" +/** SD8887 Card */ +#define CARD_SD8887 "SD8887" +/** SD8897 Card */ +#define CARD_SD8897 "SD8897" +/** SD8797 Card */ +#define CARD_SD8797 "SD8797" +/** SD8977 Card */ +#define CARD_SD8977 "SD8977" +/** SD8978 Card */ +#define CARD_SD8978 "SD8978" +/** SD8997 Card */ +#define CARD_SD8997 "SD8997" +/** SD8987 Card */ +#define CARD_SD8987 "SD8987" + +/** Declare and initialize fw_version */ +static char fw_version[32] = "0.0.0.p0"; + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ + +#define AID_NET_BT_STACK 3008 /* bluetooth stack */ + +/** Define module name */ + +#define MODULE_NAME "bt_fm_nfc" + +/** Declaration of chardev class */ +static struct class *chardev_class; + +/** Interface specific variables */ + +/** + * The global variable of a pointer to bt_private + * structure variable + **/ +bt_private *m_priv[MAX_BT_ADAPTER]; + +/** Default Driver mode */ +static int drv_mode = (DRV_MODE_BT); + +/** fw reload flag */ +int bt_fw_reload; +/** fw serial download flag */ +int bt_fw_serial = 1; + +/** Firmware flag */ +static int fw = 1; +/** default powermode */ +static int psmode = 1; +/** default BLE deep sleep */ +static int deep_sleep = 1; +/** Default CRC check control */ +static int fw_crc_check = 1; +/** Init config file (MAC address, register etc.) */ +static char *init_cfg; +/** Calibration config file (MAC address, init powe etc.) */ +static char *cal_cfg; +/** Calibration config file EXT */ +static char *cal_cfg_ext; +/** Init MAC address */ +static char *bt_mac; +static int btindrst = -1; + +/** Setting mbt_drvdbg value based on DEBUG level */ +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff & ~DBG_EVENT) +#else +#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) +#endif /* DEBUG_LEVEL2 */ +u32 mbt_drvdbg = DEFAULT_DEBUG_MASK; +#endif + +#ifdef CONFIG_OF +static int dts_enable = 1; +#endif + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int mbt_pm_keep_power = 1; +#endif + +static int btpmic = 0; + +/** Offset of sequence number in event */ +#define OFFSET_SEQNUM 4 + +/** + * @brief handle received packet + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * + * @return N/A + */ +void +bt_recv_frame(bt_private *priv, struct sk_buff *skb) +{ + struct hci_dev *hdev = NULL; + if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC) + hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer; + if (hdev) { + skb->dev = (void *)hdev; + hdev->stat.byte_rx += skb->len; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + } + return; +} + +/** + * @brief Alloc bt device + * + * @return pointer to structure mbt_dev or NULL + */ +struct mbt_dev * +alloc_mbt_dev(void) +{ + struct mbt_dev *mbt_dev; + ENTER(); + + mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL); + if (!mbt_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return mbt_dev; +} + +/** + * @brief Frees m_dev + * + * @return N/A + */ +void +free_m_dev(struct m_dev *m_dev) +{ + ENTER(); + kfree(m_dev->dev_pointer); + m_dev->dev_pointer = NULL; + LEAVE(); +} + +/** + * @brief clean up m_devs + * + * @return N/A + */ +void +clean_up_m_devs(bt_private *priv) +{ + struct m_dev *m_dev = NULL; + struct hci_dev *hdev = NULL; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if (m_dev->spec_type == BLUEZ_SPEC) { + hdev = (struct hci_dev *)m_dev->dev_pointer; + /** check if dev->name has been assigned */ + if (strstr(hdev->name, "hci")) + hci_unregister_dev(hdev); + hci_free_dev(hdev); + } + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL; + } + LEAVE(); + return; +} + +/** + * @brief This function verify the received event pkt + * + * Event format: + * +--------+--------+--------+--------+--------+ + * | Event | Length | ncmd | Opcode | + * +--------+--------+--------+--------+--------+ + * | 1-byte | 1-byte | 1-byte | 2-byte | + * +--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +check_evtpkt(bt_private *priv, struct sk_buff *skb) +{ + struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data; + struct hci_ev_cmd_complete *ec; + u16 opcode, ocf; + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (!priv->bt_dev.sendcmdflag) { + ret = BT_STATUS_FAILURE; + goto exit; + } + if (hdr->evt == HCI_EV_CMD_COMPLETE) { + ec = (struct hci_ev_cmd_complete *) + (skb->data + HCI_EVENT_HDR_SIZE); + opcode = __le16_to_cpu(ec->opcode); + ocf = hci_opcode_ocf(opcode); + PRINTM(CMD, + "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n", + opcode, ocf, priv->bt_dev.send_cmd_opcode); + if (opcode != priv->bt_dev.send_cmd_opcode) { + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (ocf) { + case BT_CMD_MODULE_CFG_REQ: + case BT_CMD_BLE_DEEP_SLEEP: + case BT_CMD_CONFIG_MAC_ADDR: + case BT_CMD_CSU_WRITE_REG: + case BT_CMD_LOAD_CONFIG_DATA: + case BT_CMD_LOAD_CONFIG_DATA_EXT: + case BT_CMD_AUTO_SLEEP_MODE: + case BT_CMD_HOST_SLEEP_CONFIG: + case BT_CMD_SDIO_PULL_CFG_REQ: + case BT_CMD_SET_EVT_FILTER: + // case BT_CMD_ENABLE_DEVICE_TESTMODE: + case BT_CMD_PMIC_CONFIGURE: + case BT_CMD_INDEPENDENT_RESET: + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter->cmd_wait_q); + break; + case BT_CMD_GET_FW_VERSION: + { + u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + snprintf(fw_version, sizeof(fw_version), + "%u.%u.%u.p%u", pos[2], pos[1], pos[0], + pos[3]); + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } + case BT_CMD_RESET: + case BT_CMD_ENABLE_WRITE_SCAN: + { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + if (priv->adapter->wait_event_timeout == TRUE) { + wake_up(&priv->adapter->cmd_wait_q); + priv->adapter->wait_event_timeout = + FALSE; + } else + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + break; + case BT_CMD_HOST_SLEEP_ENABLE: + priv->bt_dev.sendcmdflag = FALSE; + break; + default: + /** Ignore command not defined but send by driver */ + if (opcode == priv->bt_dev.send_cmd_opcode) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } else { + ret = BT_STATUS_FAILURE; + } + break; + } + } else + ret = BT_STATUS_FAILURE; +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** +* @brief This function stores the FW dumps received from events +* +* @param priv A pointer to bt_private structure +* @param skb A pointer to rx skb +* +* @return N/A +*/ +void +bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len) +{ + struct file *pfile_fwdump = NULL; + loff_t pos = 0; + u16 seqnum = 0; + struct timeval t; + u32 sec; + + ENTER(); + + seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM)); + + if (priv->adapter->fwdump_fname && seqnum != 1) { + pfile_fwdump = + filp_open((const char *)priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file.\n"); + LEAVE(); + return; + } + } else { + if (!priv->adapter->fwdump_fname) { + gfp_t flag; + flag = (in_atomic() || + irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + priv->adapter->fwdump_fname = kzalloc(64, flag); + } else + memset(priv->adapter->fwdump_fname, 0, 64); + + do_gettimeofday(&t); + sec = (u32)t.tv_sec; + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/var/log/bt_fwdump_", sec); + pfile_fwdump = + filp_open(priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/data/bt_fwdump_", sec); + pfile_fwdump = + filp_open((const char *)priv->adapter-> + fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + } + } + + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file\n"); + LEAVE(); + return; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_fwdump, buf, len, &pos); +#else + kernel_write(pfile_fwdump, buf, len, &pos); +#endif + filp_close(pfile_fwdump, NULL); + LEAVE(); + return; +} + +/** + * @brief This function process the received event + * + * Event format: + * +--------+--------+--------+--------+-----+ + * | EC | Length | Data | + * +--------+--------+--------+--------+-----+ + * | 1-byte | 1-byte | n-byte | + * +--------+--------+--------+--------+-----+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_event(bt_private *priv, struct sk_buff *skb) +{ + int ret = BT_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + BT_EVENT *pevent; + + ENTER(); + if (!m_dev) { + PRINTM(CMD, "BT: bt_process_event without m_dev\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pevent = (BT_EVENT *)skb->data; + if (pevent->EC != 0xff) { + PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC); + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (pevent->data[0]) { + case BT_CMD_AUTO_SLEEP_MODE: + if (pevent->data[2] == BT_STATUS_SUCCESS) { + if (pevent->data[1] == BT_PS_ENABLE) + priv->adapter->psmode = 1; + else + priv->adapter->psmode = 0; + PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name, + (priv->adapter->psmode) ? "Enable" : "Disable"); + + } else { + PRINTM(CMD, "BT: PS Mode Command Fail %s\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_CONFIG: + if (pevent->data[3] == BT_STATUS_SUCCESS) { + PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n", + m_dev->name, pevent->data[1], pevent->data[2]); + } else { + PRINTM(CMD, "BT: %s: HSCFG Command Fail\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_ENABLE: + if (pevent->data[1] == BT_STATUS_SUCCESS) { + priv->adapter->hs_state = HS_ACTIVATED; + if (priv->adapter->suspend_fail == FALSE) { +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED + bt_is_suspended(priv); +#endif +#endif +#endif + if (priv->adapter->wait_event_timeout) { + wake_up(&priv->adapter->cmd_wait_q); + priv->adapter->wait_event_timeout = + FALSE; + } else + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + + } + if (priv->adapter->psmode) + priv->adapter->ps_state = PS_SLEEP; + PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n", + m_dev->name); + + } else { + PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name); + } + break; + case BT_CMD_MODULE_CFG_REQ: + if ((priv->bt_dev.sendcmdflag == TRUE) && + ((pevent->data[1] == MODULE_BRINGUP_REQ) + || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) { + if (pevent->data[1] == MODULE_BRINGUP_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2] && (pevent->data[2] != + MODULE_CFG_RESP_ALREADY_UP)) + ? "Bring up Fail" : "Bring up success"); + priv->bt_dev.devType = pevent->data[3]; + PRINTM(CMD, "devType:%s\n", + (pevent->data[3] == + DEV_TYPE_AMP) ? "AMP controller" : + "BR/EDR controller"); + priv->bt_dev.devFeature = pevent->data[4]; + PRINTM(CMD, "devFeature: %s, %s, %s" + "\n", + ((pevent-> + data[4] & DEV_FEATURE_BT) ? + "BT Feature" : "No BT Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BTAMP) ? + "BTAMP Feature" : "No BTAMP Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BLE) ? + "BLE Feature" : "No BLE Feature") + ); + } + if (pevent->data[1] == MODULE_SHUTDOWN_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2]) ? "Shut down Fail" + : "Shut down success"); + + } + if (pevent->data[2]) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + } else { + PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n"); + ret = BT_STATUS_FAILURE; + } + break; + case BT_EVENT_POWER_STATE: + if (pevent->data[1] == BT_PS_SLEEP) + priv->adapter->ps_state = PS_SLEEP; + if (priv->adapter->ps_state == PS_SLEEP + && (priv->card_type == CARD_TYPE_SD8887) + ) + os_sched_timeout(5); + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); + + break; + case BT_CMD_SDIO_PULL_CFG_REQ: + if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS) + PRINTM(CMD, "BT: %s: SDIO pull configuration success\n", + m_dev->name); + + else { + PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n", + m_dev->name); + + } + break; + default: + PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0], + m_dev->name); + ret = BT_STATUS_FAILURE; + break; + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** + * @brief This function save the dump info to file + * + * @param dir_name directory name + * @param file_name file_name + * @param buf buffer + * @param buf_len buffer length + * + * @return 0 --success otherwise fail + */ +int +bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len) +{ + int ret = BT_STATUS_SUCCESS; + struct file *pfile = NULL; + u8 name[64]; + loff_t pos; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(ERROR, "Can't save dump info to file\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + snprintf((char *)name, sizeof(name), "%s/%s", dir_name, file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + snprintf((char *)name, sizeof(name), "%s/%s", "/var", + file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(ERROR, "Create Dump file for %s error\n", name); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name); + + pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, (const char __user *)buf, buf_len, &pos); + set_fs(fs); +#else + kernel_write(pfile, (const char __user *)buf, buf_len, &pos); +#endif + filp_close(pfile, NULL); + + PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name); + +done: + LEAVE(); + return ret; +} + +#define DEBUG_HOST_READY 0xEE +#define DEBUG_FW_DONE 0xFF +#define DUMP_MAX_POLL_TRIES 200 + +#define DEBUG_DUMP_CTRL_REG_8897 0xE2 +#define DEBUG_DUMP_START_REG_8897 0xE3 +#define DEBUG_DUMP_END_REG_8897 0xEA +#define DEBUG_DUMP_CTRL_REG_8887 0xA2 +#define DEBUG_DUMP_START_REG_8887 0xA3 +#define DEBUG_DUMP_END_REG_8887 0xAA +#define DEBUG_DUMP_CTRL_REG_8977 0xF0 +#define DEBUG_DUMP_START_REG_8977 0xF1 +#define DEBUG_DUMP_END_REG_8977 0xF8 +#define DEBUG_DUMP_CTRL_REG_8978 0xF0 +#define DEBUG_DUMP_START_REG_8978 0xF1 +#define DEBUG_DUMP_END_REG_8978 0xF8 +#define DEBUG_DUMP_CTRL_REG_8997 0xF0 +#define DEBUG_DUMP_START_REG_8997 0xF1 +#define DEBUG_DUMP_END_REG_8997 0xF8 +#define DEBUG_DUMP_CTRL_REG_8987 0xF0 +#define DEBUG_DUMP_START_REG_8987 0xF1 +#define DEBUG_DUMP_END_REG_8987 0xF8 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_APU_REGS = 3, + DUMP_TYPE_CIU_REGS = 4, + DUMP_TYPE_ICU_REGS = 5, + DUMP_TYPE_MAC_REGS = 6, + DUMP_TYPE_EXTEND_7 = 7, + DUMP_TYPE_EXTEND_8 = 8, + DUMP_TYPE_EXTEND_9 = 9, + DUMP_TYPE_EXTEND_10 = 10, + DUMP_TYPE_EXTEND_11 = 11, + DUMP_TYPE_EXTEND_12 = 12, + DUMP_TYPE_EXTEND_13 = 13, + DUMP_TYPE_EXTEND_LAST = 14 +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 + +/** Memory type mapping structure */ +typedef struct { + /** memory name */ + u8 mem_name[MAX_NAME_LEN]; + /** memory pointer */ + u8 *mem_Ptr; + /** file structure */ + struct file *pfile_mem; + /** donbe flag */ + u8 done_flag; + /** dump type */ + u8 type; +} memory_type_mapping; + +memory_type_mapping bt_mem_type_mapping_tbl[] = { + {"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM}, + {"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM}, + {"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM}, + {"APU", NULL, NULL, 0xF3, FW_DUMP_TYPE_REG_APU}, + {"CIU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_CIU}, + {"ICU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_ICU}, + {"MAC", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_MAC}, + {"EXT7", NULL, NULL, 0xF7}, + {"EXT8", NULL, NULL, 0xF8}, + {"EXT9", NULL, NULL, 0xF9}, + {"EXT10", NULL, NULL, 0xFA}, + {"EXT11", NULL, NULL, 0xFB}, + {"EXT12", NULL, NULL, 0xFC}, + {"EXT13", NULL, NULL, 0xFD}, + {"EXTLAST", NULL, NULL, 0xFE}, +}; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +/** + * @brief This function read/write firmware via cmd52 + * + * @param phandle A pointer to moal_handle + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status +bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag) +{ + int ret = 0; + int tries = 0; + u8 ctrl_data = 0; + u8 dbg_dump_ctrl_reg = 0; + + if (priv->card_type == CARD_TYPE_SD8887) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8887; + else if (priv->card_type == CARD_TYPE_SD8897) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8897; + else if (priv->card_type == CARD_TYPE_SD8977) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8977; + else if (priv->card_type == CARD_TYPE_SD8978) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8978; + else if (priv->card_type == CARD_TYPE_SD8997) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8997; + else if (priv->card_type == CARD_TYPE_SD8987) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8987; + + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) { + ctrl_data = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)-> + func, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != DEBUG_HOST_READY) { + PRINTM(INFO, + "The ctrl reg was changed, re-try again!\n"); + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev. + card)->func, DEBUG_HOST_READY, + dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } + udelay(100); + } + if (ctrl_data == DEBUG_HOST_READY) { + PRINTM(ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void +bt_dump_firmware_info_v2(bt_private *priv) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr = NULL; + u8 dump_num = 0; + u8 idx = 0; + u8 doneflag = 0; + rdwr_status stat; + u8 i = 0; + u8 read_reg = 0; + u32 memory_size = 0; + u8 path_name[64], file_name[32]; + u8 *end_ptr = NULL; + u8 dbg_dump_start_reg = 0; + u8 dbg_dump_end_reg = 0; + + if (!priv) { + PRINTM(ERROR, "Could not dump firmwware info\n"); + return; + } + + if ((priv->card_type != CARD_TYPE_SD8887) && + (priv->card_type != CARD_TYPE_SD8897) + && (priv->card_type != CARD_TYPE_SD8977) && + (priv->card_type != CARD_TYPE_SD8997) + && (priv->card_type != CARD_TYPE_SD8987) && + (priv->card_type != CARD_TYPE_SD8978)) { + PRINTM(MSG, "card_type %d don't support FW dump\n", + priv->card_type); + return; + } + + memset(path_name, 0, sizeof(path_name)); + strcpy((char *)path_name, "/data"); + PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name); + + if (priv->card_type == CARD_TYPE_SD8887) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8887; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8887; + } else if (priv->card_type == CARD_TYPE_SD8897) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8897; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8897; + } else if (priv->card_type == CARD_TYPE_SD8977) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8977; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8977; + } else if (priv->card_type == CARD_TYPE_SD8978) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8978; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8978; + } else if (priv->card_type == CARD_TYPE_SD8997) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8997; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8997; + } else if (priv->card_type == CARD_TYPE_SD8987) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8987; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8987; + } + + sbi_wakeup_firmware(priv); + sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + priv->fw_dump = TRUE; + /* start dump fw memory */ + PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n"); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + reg = dbg_dump_start_reg; + dump_num = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ MEM NUM ERR\n"); + goto done; + } + + /* read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + if (RDWR_STATUS_FAILURE == + bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + memory_size = 0; + reg = dbg_dump_start_reg; + for (i = 0; i < 4; i++) { + read_reg = + sdio_readb(((struct sdio_mmc_card *)priv-> + bt_dev.card)->func, reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + if (memory_size == 0) { + PRINTM(MSG, "Firmware Dump Finished!\n"); + break; + } else { + PRINTM(MSG, "%s_SIZE=0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + memory_size); + bt_mem_type_mapping_tbl[idx].mem_Ptr = + vmalloc(memory_size + 1); + if ((ret != BT_STATUS_SUCCESS) || + !bt_mem_type_mapping_tbl[idx].mem_Ptr) { + PRINTM(ERROR, + "Error: vmalloc %s buffer failed!!!\n", + bt_mem_type_mapping_tbl[idx].mem_name); + goto done; + } + dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr; + end_ptr = dbg_ptr + memory_size; + } + doneflag = bt_mem_type_mapping_tbl[idx].done_flag; + PRINTM(MSG, "Start %s output, please wait...\n", + bt_mem_type_mapping_tbl[idx].mem_name); + do { + stat = bt_cmd52_rdwr_firmware(priv, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = + sdio_readb(((struct sdio_mmc_card *) + priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + PRINTM(MSG, + "pre-allocced buf is not enough\n"); + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MSG, "%s done:" + "size = 0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + (unsigned int)(dbg_ptr - + bt_mem_type_mapping_tbl + [idx].mem_Ptr)); + memset(file_name, 0, sizeof(file_name)); + snprintf((char *)file_name, sizeof(file_name), + "%s%s", "file_bt_", + bt_mem_type_mapping_tbl[idx].mem_name); + if (BT_STATUS_SUCCESS != + bt_save_dump_info_to_file((char *)path_name, + (char *)file_name, + bt_mem_type_mapping_tbl + [idx].mem_Ptr, + memory_size)) + PRINTM(MSG, + "Can't save dump file %s in %s\n", + file_name, path_name); + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + break; + } + } while (1); + } + PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n"); + /* end dump fw memory */ +done: + priv->fw_dump = FALSE; + sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + for (idx = 0; idx < dump_num; idx++) { + if (bt_mem_type_mapping_tbl[idx].mem_Ptr) { + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + } + } + PRINTM(MSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function shows debug info for timeout of command sending. + * + * @param adapter A pointer to bt_private + * @param cmd Timeout command id + * + * @return N/A + */ +static void +bt_cmd_timeout_func(bt_private *priv, u16 cmd) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + + adapter->num_cmd_timeout++; + + PRINTM(ERROR, "Version = %s\n", adapter->drv_ver); + PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd); + PRINTM(ERROR, "Number of command timeout = %d\n", + adapter->num_cmd_timeout); + PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter); + PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode); + PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state); + PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state); + PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip); + PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail); + PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended); + PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries); + PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete); + PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv); + PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done); + PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending); + PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg); + bt_dump_sdio_regs(priv); + LEAVE(); +} + +/** + * @brief This function queue frame + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to sk_buff structure + * + * @return N/A + */ +static void +bt_queue_frame(bt_private *priv, struct sk_buff *skb) +{ + skb_queue_tail(&priv->adapter->tx_queue, skb); +} + +/** + * @brief This function send reset cmd to firmware + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_reset_command(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET); + pcmd->length = 0x00; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, 3); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue Reset Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Reset timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_RESET); + } else { + PRINTM(CMD, "BT: Reset Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends module cfg cmd to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param subcmd sub command + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_module_cfg_cmd(bt_private *priv, int subcmd) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ); + pcmd->length = 1; + pcmd->data[0] = subcmd; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue module cfg Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + /* + On some Android platforms certain delay is needed for HCI daemon to + remove this module and close itself gracefully. Otherwise it hangs. + This 10ms delay is a workaround for such platforms as the root cause + has not been found yet. */ + mdelay(10); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n", + subcmd, priv->bt_dev.sendcmdflag); + bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ); + } else { + PRINTM(CMD, "BT: module cfg Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables power save mode + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_ps(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE); + if (priv->bt_dev.psmode) + pcmd->data[0] = BT_PS_ENABLE; + else + pcmd->data[0] = BT_PS_DISABLE; + if (priv->bt_dev.idle_timeout) { + pcmd->length = 3; + pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff); + pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8; + } else { + pcmd->length = 1; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: psmode timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends hscfg command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_hscfg_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG); + pcmd->length = 2; + pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8; + pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: HSCFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends sdio pull ctrl command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_sdio_pull_ctrl_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ); + pcmd->length = 4; + pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff); + pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8; + pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16; + pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, + "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0], + pcmd->data[3], pcmd->data[2]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends command to configure PMIC + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_pmic_configure(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: PMIC Configure timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables host sleep + * + * @param priv A pointer to bt_private structure + * @param is_shutdown indicate shutdown mode + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_hs(bt_private *priv, bool is_shutdown) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + priv->adapter->suspend_fail = FALSE; + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->adapter->wait_event_timeout = is_shutdown; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + PRINTM(CMD, "Queue hs enable Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (is_shutdown) { + if (!os_wait_timeout + (priv->adapter->cmd_wait_q, priv->adapter->hs_state, + WAIT_UNTIL_HS_STATE_CHANGED)) { + PRINTM(MSG, "BT: Enable host sleep timeout:\n"); + priv->adapter->wait_event_timeout = FALSE; + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE); + } + } else { + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->hs_state, + WAIT_UNTIL_HS_STATE_CHANGED)) { + PRINTM(MSG, "BT: Enable host sleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE); + } + } + OS_INT_DISABLE; + if ((priv->adapter->hs_state == HS_ACTIVATED) || + (priv->adapter->is_suspended == TRUE)) { + OS_INT_RESTORE; + PRINTM(MSG, "BT: suspend success! skip=%d\n", + priv->adapter->hs_skip); + } else { + priv->adapter->suspend_fail = TRUE; + OS_INT_RESTORE; + priv->adapter->hs_skip++; + ret = BT_STATUS_FAILURE; + PRINTM(MSG, + "BT: suspend skipped! " + "state=%d skip=%d ps_state= %d WakeupTries=%d\n", + priv->adapter->hs_state, priv->adapter->hs_skip, + priv->adapter->ps_state, priv->adapter->WakeupTries); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Set Evt Filter + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_evt_filter(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER); + pcmd->length = 0x03; + pcmd->data[0] = 0x02; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set Evt Filter timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Write Scan - Page and Inquiry + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_write_scan(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN); + pcmd->length = 0x01; + pcmd->data[0] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Write Scan timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Device under test mode + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_device_under_testmode(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE); + pcmd->length = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables test mode and send cmd + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_test_mode(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + /** Set Evt Filter Command */ + ret = bt_set_evt_filter(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n"); + goto exit; + } + + /** Enable Write Scan Command */ + ret = bt_enable_write_scan(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n"); + goto exit; + } + + /** Enable Device under test mode */ + ret = bt_enable_device_under_testmode(priv); + if (ret != BT_STATUS_SUCCESS) + PRINTM(ERROR, + "BT test_mode: Enable device under testmode fail\n"); + +exit: + LEAVE(); + return ret; +} + +#define DISABLE_RESET 0x0 +#define ENABLE_OUTBAND_RESET 0x1 +#define ENABLE_INBAND_RESET 0x02 +#define DEFAULT_GPIO 0xff +/** + * @brief This function set GPIO pin + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_independent_reset(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + u8 mode, gpio; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET); + mode = btindrst & 0xff; + gpio = (btindrst & 0xff00) >> 8; + if (mode == ENABLE_OUTBAND_RESET) { + pcmd->data[0] = ENABLE_OUTBAND_RESET; + if (!gpio) + pcmd->data[1] = DEFAULT_GPIO; + else + pcmd->data[1] = gpio; + } else if (mode == ENABLE_INBAND_RESET) { + pcmd->data[0] = ENABLE_INBAND_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else if (mode == DISABLE_RESET) { + pcmd->data[0] = DISABLE_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else { + PRINTM(WARN, "Unsupport mode\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio); + pcmd->length = 2; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Independent reset : timeout!\n"); + bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets ble deepsleep mode + * + * @param priv A pointer to bt_private structure + * @param mode TRUE/FALSE + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_ble_deepsleep(bt_private *priv, int mode) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_BLE_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_BLE_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP); + pcmd->length = 1; + pcmd->deepsleep = mode; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_BLE_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode, + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function gets FW version + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_get_fw_version(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION); + pcmd->length = 0x01; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, 4); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Get FW version: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets mac address + * + * @param priv A pointer to bt_private structure + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_mac_address(bt_private *priv, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + int i = 0; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR); + pcmd->length = 8; + pcmd->cmd_type = MRVL_VENDOR_PKT; + pcmd->cmd_len = 6; + for (i = 0; i < 6; i++) + pcmd->data[i] = mac[5 - i]; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_HCI_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac), + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set mac addr: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + int i = 0; + /* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01 + 0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0xF0}; */ + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA); + pcmd->length = 0x20; + pcmd->data[0] = 0x00; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x00; + pcmd->data[3] = 0x1C; + /* swip cal-data byte */ + for (i = 4; i < 32; i++) + pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i)); + if (mac != NULL) { + pcmd->data[2] = 0x01; /* skip checksum */ + for (i = 24; i < 30; i++) + pcmd->data[i] = mac[29 - i]; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate EXT data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + + ENTER(); + + if (cfg_data_len > BT_CMD_DATA_LEN) { + PRINTM(WARN, "cfg_data_len is too long exceed %d.\n", + BT_CMD_DATA_LEN); + ret = BT_STATUS_FAILURE; + goto exit; + } + + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT); + pcmd->length = cfg_data_len; + + memcpy(pcmd->data, config_data, cfg_data_len); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function writes value to CSU registers + * + * @param priv A pointer to bt_private structure + * @param type reg type + * @param offset register address + * @param value register value to write + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CSU_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CSU_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG); + pcmd->length = 7; + pcmd->type = type; + pcmd->offset[0] = (offset & 0x000000ff); + pcmd->offset[1] = (offset & 0x0000ff00) >> 8; + pcmd->offset[2] = (offset & 0x00ff0000) >> 16; + pcmd->offset[3] = (offset & 0xff000000) >> 24; + pcmd->value[0] = (value & 0x00ff); + pcmd->value[1] = (value & 0xff00) >> 8; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_CSU_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n", + type, offset, value); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Set CSU reg timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function used to restore tx_queue + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_restore_tx_queue(bt_private *priv) +{ + struct sk_buff *skb = NULL; + while (!skb_queue_empty(&priv->adapter->pending_queue)) { + skb = skb_dequeue(&priv->adapter->pending_queue); + if (skb) + bt_queue_frame(priv, skb); + } + wake_up_interruptible(&priv->MainThread.waitQ); +} + +/** + * @brief This function used to send command to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_prepare_command(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (priv->bt_dev.hscfgcmd) { + priv->bt_dev.hscfgcmd = 0; + ret = bt_send_hscfg_cmd(priv); + } + if (priv->bt_dev.pscmd) { + priv->bt_dev.pscmd = 0; + ret = bt_enable_ps(priv); + } + if (priv->bt_dev.sdio_pull_ctrl) { + priv->bt_dev.sdio_pull_ctrl = 0; + ret = bt_send_sdio_pull_ctrl_cmd(priv); + } + if (priv->bt_dev.hscmd) { + priv->bt_dev.hscmd = 0; + if (priv->bt_dev.hsmode) + ret = bt_enable_hs(priv, FALSE); + else { + ret = sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + } + } + if (priv->bt_dev.test_mode) { + priv->bt_dev.test_mode = 0; + ret = bt_enable_test_mode(priv); + } + LEAVE(); + return ret; +} + +/** + * @brief Update tx state + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to sk_buff structure + * + * @return N/A + */ +static void +update_stat_byte_tx(bt_private *priv, struct sk_buff *skb) +{ + ((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat. + byte_tx += skb->len; +} + +/** + * @brief Update tx error state + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to sk_buff structure + * + * @return N/A + */ +static void +update_stat_err_tx(bt_private *priv, struct sk_buff *skb) +{ + ((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat. + err_tx++; +} + +/** @brief This function processes a single packet + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to skb which includes TX packet + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +send_single_packet(bt_private *priv, struct sk_buff *skb) +{ + int ret; + struct sk_buff *new_skb = NULL; + ENTER(); + if (!skb || !skb->data) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) { + PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, + BT_UPLD_SIZE); + LEAVE(); + return BT_STATUS_FAILURE; + } + if (skb_headroom(skb) < BT_HEADER_LEN) { + new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN); + if (!new_skb) { + PRINTM(ERROR, "TX error: realloc_headroom failed %d\n", + BT_HEADER_LEN); + kfree_skb(skb); + LEAVE(); + return BT_STATUS_FAILURE; + } else { + if (new_skb != skb) + dev_kfree_skb_any(skb); + skb = new_skb; + } + } + /* This is SDIO/PCIE specific header length: byte[3][2][1], * type: + byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) + */ + skb_push(skb, BT_HEADER_LEN); + skb->data[0] = (skb->len & 0x0000ff); + skb->data[1] = (skb->len & 0x00ff00) >> 8; + skb->data[2] = (skb->len & 0xff0000) >> 16; + skb->data[3] = bt_cb(skb)->pkt_type; + if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT) + PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n", + __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len); + ret = sbi_host_to_card(priv, skb->data, skb->len); + if (ret == BT_STATUS_FAILURE) + update_stat_err_tx(priv, skb); + else + update_stat_byte_tx(priv, skb); + if (ret != BT_STATUS_PENDING) + kfree_skb(skb); + LEAVE(); + return ret; +} + +#ifdef CONFIG_OF +/** + * @brief This function read the initial parameter from device tress + * + * + * @return N/A + */ +static void +bt_init_from_dev_tree(void) +{ + struct device_node *dt_node = NULL; + struct property *prop; + u32 data; + const char *string_data; + + ENTER(); + + if (!dts_enable) { + PRINTM(CMD, "DTS is disabled!"); + return; + } + + dt_node = of_find_node_by_name(NULL, "sd8xxx-bt"); + if (!dt_node) { + LEAVE(); + return; + } + for_each_property_of_node(dt_node, prop) { +#ifdef DEBUG_LEVEL1 + if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(CMD, "mbt_drvdbg=0x%x\n", data); + mbt_drvdbg = data; + } + } +#endif + else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + init_cfg = (char *)string_data; + PRINTM(CMD, "init_cfg=%s\n", init_cfg); + } + } else if (!strncmp + (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg_ext = (char *)string_data; + PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext); + } + } else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg = (char *)string_data; + PRINTM(CMD, "cal_cfg=%s\n", cal_cfg); + } + } else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + bt_mac = (char *)string_data; + PRINTM(CMD, "bt_mac=%s\n", bt_mac); + } + } else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btindrst = data; + PRINTM(CMD, "btindrst=%d\n", btindrst); + } + } else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btpmic = data; + PRINTM(CMD, "btpmic=%d\n", btpmic); + } + } + } + LEAVE(); + return; +} +#endif + +/** + * @brief This function initializes the adapter structure + * and set default value to the member of adapter. + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +static void +bt_init_adapter(bt_private *priv) +{ + ENTER(); +#ifdef CONFIG_OF + bt_init_from_dev_tree(); +#endif + skb_queue_head_init(&priv->adapter->tx_queue); + skb_queue_head_init(&priv->adapter->pending_queue); + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + priv->adapter->fwdump_fname = NULL; + init_waitqueue_head(&priv->adapter->cmd_wait_q); + LEAVE(); +} + +/** + * @brief This function initializes firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (fw == 0) { + sbi_enable_host_int(priv); + goto done; + } + sbi_disable_host_int(priv); + if ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + priv->fw_crc_check = fw_crc_check; + if (sbi_download_fw(priv)) { + PRINTM(ERROR, " FW failed to be download!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +#define FW_POLL_TRIES 100 +#define SD8897_FW_RESET_REG 0x0E8 +#define SD8887_FW_RESET_REG 0x0B6 +#define SD8977_SD8978_SD8997_FW_RESET_REG 0x0EE +#define SD8887_SD8897_FW_RESET_VAL 1 +#define SD8977_SD8978_SD8997_FW_RESET_VAL 0x99 + +/** + * @brief This function reload firmware + * + * @param priv A pointer to bt_private + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +static int +bt_reload_fw(bt_private *priv, int mode) +{ + int ret = 0, tries = 0; + u8 value = 1; + u32 reset_reg = 0; + u8 reset_val = 0; + + ENTER(); + if ((mode != FW_RELOAD_SDIO_INBAND_RESET) && + (mode != FW_RELOAD_NO_EMULATION)) { + PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode); + return -EFAULT; + } + + /** flush pending tx_queue */ + skb_queue_purge(&priv->adapter->tx_queue); + if (mode == FW_RELOAD_SDIO_INBAND_RESET) { + if (priv->card_type == CARD_TYPE_SD8887) { + reset_reg = SD8887_FW_RESET_REG; + reset_val = SD8887_SD8897_FW_RESET_VAL; + } else if (priv->card_type == CARD_TYPE_SD8897) { + reset_reg = SD8897_FW_RESET_REG; + reset_val = SD8887_SD8897_FW_RESET_VAL; + } else if ((priv->card_type == CARD_TYPE_SD8977) || + (priv->card_type == CARD_TYPE_SD8997) || + (priv->card_type == CARD_TYPE_SD8978) || + (priv->card_type == CARD_TYPE_SD8987)) { + reset_reg = SD8977_SD8978_SD8997_FW_RESET_REG; + reset_val = SD8977_SD8978_SD8997_FW_RESET_VAL; + } + sbi_disable_host_int(priv); + /** Wake up firmware firstly */ + sbi_wakeup_firmware(priv); + + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = sd_write_reg(priv, reset_reg, 0xba); + if (!ret) { + ret = sd_read_reg(priv, reset_reg, &value); + if (!ret && (value == 0xba)) { + PRINTM(MSG, "Fw wake up\n"); + break; + } + } + udelay(1000); + } + + ret = sd_write_reg(priv, reset_reg, reset_val); + if (ret) { + PRINTM(ERROR, "Failed to write register.\n"); + goto done; + } + + /** Poll register around 1 ms */ + for (; tries < FW_POLL_TRIES; ++tries) { + ret = sd_read_reg(priv, reset_reg, &value); + if (ret) { + PRINTM(ERROR, "Failed to read register.\n"); + goto done; + } + if (value == 0) + /** FW is ready */ + break; + udelay(1000); + } + if (value) { + PRINTM(ERROR, + "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + } + + sbi_enable_host_int(priv); + /** reload FW */ + ret = bt_init_fw(priv); + if (ret) { + PRINTM(ERROR, "Re download firmware failed.\n"); + ret = -EFAULT; + } + LEAVE(); + return ret; +done: + sbi_enable_host_int(priv); + LEAVE(); + return ret; +} + +/** + * @brief This function request to reload firmware + * + * @param priv A pointer to bt_private + * @param mode fw reload mode. + * + * @return N/A + */ +void +bt_request_fw_reload(bt_private *priv, int mode) +{ + ENTER(); + if (mode == FW_RELOAD_WITH_EMULATION) { + bt_fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MSG, "BT: FW reload with re-emulation...\n"); + LEAVE(); + return; + } + /** Reload FW */ + priv->fw_reload = TRUE; + if (bt_reload_fw(priv, mode)) { + PRINTM(ERROR, "FW reload fail\n"); + goto done; + } + priv->fw_reload = FALSE; + /** Other operation here? */ +done: + LEAVE(); + return; +} + +/** + * @brief This function frees the structure of adapter + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_free_adapter(bt_private *priv) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + kfree(adapter->tx_buffer); + kfree(adapter->hw_regs_buf); + /* Free allocated memory for fwdump filename */ + if (adapter->fwdump_fname) { + kfree(adapter->fwdump_fname); + adapter->fwdump_fname = NULL; + } + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; + + LEAVE(); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +/** + * @brief This function handles the BT ioctl + * + * @param hdev A pointer to hci_dev structure + * @cmd ioctl cmd + * @arg argument + * @return -ENOIOCTLCMD + */ +static int +bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) +{ + ENTER(); + LEAVE(); + return -ENOIOCTLCMD; +} +#endif + +/** + * @brief This function handles the wrapper_dev ioctl + * + * @param hev A pointer to wrapper_dev structure + * @cmd ioctl cmd + * @arg argument + * @return -ENOIOCTLCMD + */ +static int +mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg) +{ + bt_private *priv = NULL; + int ret = 0; + + ENTER(); + + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n"); + ret = -ENODEV; + goto done; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n", + m_dev->flags); + ret = -EBUSY; + goto done; + } + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { + default: + break; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles BT destruct + * + * @param hdev A pointer to hci_dev structure + * + * @return N/A + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) +static void +bt_destruct(struct hci_dev *hdev) +{ + ENTER(); + LEAVE(); + return; +} +#endif + +/** + * @brief This function handles wrapper device destruct + * + * @param m_dev A pointer to m_dev structure + * + * @return N/A + */ +static void +mdev_destruct(struct m_dev *m_dev) +{ + ENTER(); + LEAVE(); + return; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +/** + * @brief This function handles the BT transmit + * + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +bt_send_frame(struct sk_buff *skb) +#else +/** + * @brief This function handles the BT transmit + * + * @param hdev A pointer to hci_dev structure + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +bt_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +#endif +{ + bt_private *priv = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + struct hci_dev *hdev = (struct hci_dev *)skb->dev; +#else + skb->dev = (void *)hdev; +#endif + + ENTER(); + PRINTM(DATA, "bt_send_frame %s: Type=%d, len=%d\n", hdev->name, + bt_cb(skb)->pkt_type, skb->len); + DBG_HEXDUMP(CMD_D, "bt_send_frame", skb->data, skb->len); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (!hdev || !hci_get_drvdata(hdev)) { +#else + if (!hdev || !hdev->driver_data) { +#endif + PRINTM(ERROR, "Frame for unknown HCI device (hdev=NULL)\n"); + LEAVE(); + return -ENODEV; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + priv = (bt_private *)hci_get_drvdata(hdev); +#else + priv = (bt_private *)hdev->driver_data; +#endif + if (!test_bit(HCI_RUNNING, &hdev->flags)) { + PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n", + hdev->flags); + LEAVE(); + return -EBUSY; + } + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + } + if (priv->adapter->tx_lock == TRUE) + skb_queue_tail(&priv->adapter->pending_queue, skb); + else + bt_queue_frame(priv, skb); + + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handles the wrapper device transmit + * + * @param m_dev A pointer to m_dev structure + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb) +{ + bt_private *priv = NULL; + + ENTER(); + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n"); + LEAVE(); + return -ENODEV; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n", + m_dev->flags); + LEAVE(); + return -EBUSY; + } + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + m_dev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + m_dev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + m_dev->stat.sco_tx++; + break; + } + + if (priv->adapter->tx_lock == TRUE) + skb_queue_tail(&priv->adapter->pending_queue, skb); + else + bt_queue_frame(priv, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function flushes the transmit queue + * + * @param hdev A pointer to hci_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +bt_flush(struct hci_dev *hdev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + bt_private *priv = (bt_private *)hci_get_drvdata(hdev); +#else + bt_private *priv = (bt_private *)hdev->driver_data; +#endif + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->pending_queue); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function flushes the transmit queue + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_flush(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->pending_queue); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function closes the bluetooth device + * + * @param hdev A pointer to hci_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +bt_close(struct hci_dev *hdev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + bt_private *priv = (bt_private *)hci_get_drvdata(hdev); +#else + bt_private *priv = (bt_private *)hdev->driver_data; +#endif + + ENTER(); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0) + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) { + LEAVE(); + return BT_STATUS_SUCCESS; + } +#endif + skb_queue_purge(&priv->adapter->tx_queue); + + module_put(THIS_MODULE); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function closes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_close(struct m_dev *m_dev) +{ + + ENTER(); + mdev_req_lock(m_dev); + if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + + if (m_dev->flush) + m_dev->flush(m_dev); + /* wait up pending read and unregister char dev */ + wake_up_interruptible(&m_dev->req_wait_q); + /* Drop queues */ + skb_queue_purge(&m_dev->rx_q); + if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + module_put(THIS_MODULE); + m_dev->flags = 0; + mdev_req_unlock(m_dev); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function opens the bluetooth device + * + * @param hdev A pointer to hci_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +static int +bt_open(struct hci_dev *hdev) +{ + ENTER(); + if (try_module_get(THIS_MODULE) == 0) + return BT_STATUS_FAILURE; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0) + set_bit(HCI_RUNNING, &hdev->flags); +#endif + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function opens the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +static int +mdev_open(struct m_dev *m_dev) +{ + ENTER(); + + if (try_module_get(THIS_MODULE) == 0) + return BT_STATUS_FAILURE; + + set_bit(HCI_RUNNING, &m_dev->flags); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function queries the wrapper device + * + * @param m_dev A pointer to m_dev structure + * @param arg arguement + * + * @return BT_STATUS_SUCCESS or other + */ +void +mdev_query(struct m_dev *m_dev, void *arg) +{ + struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer; + + ENTER(); + if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type))) + PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n"); + + LEAVE(); +} + +/** + * @brief This function initializes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +void +init_m_dev(struct m_dev *m_dev) +{ + m_dev->dev_pointer = NULL; + m_dev->driver_data = NULL; + m_dev->dev_type = 0; + m_dev->spec_type = 0; + skb_queue_head_init(&m_dev->rx_q); + init_waitqueue_head(&m_dev->req_wait_q); + init_waitqueue_head(&m_dev->rx_wait_q); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + init_MUTEX(&m_dev->req_lock); +#else + sema_init(&m_dev->req_lock, 1); +#endif + spin_lock_init(&m_dev->rxlock); + memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats)); + m_dev->open = mdev_open; + m_dev->close = mdev_close; + m_dev->flush = mdev_flush; + m_dev->send = mdev_send_frame; + m_dev->destruct = mdev_destruct; + m_dev->ioctl = mdev_ioctl; + m_dev->query = mdev_query; + m_dev->owner = THIS_MODULE; + +} + +/** + * @brief This function handles the major job in bluetooth driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to bt_thread structure + * @return BT_STATUS_SUCCESS + */ +static int +bt_service_main_thread(void *data) +{ + bt_thread *thread = data; + bt_private *priv = thread->priv; + bt_adapter *adapter = priv->adapter; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + wait_queue_t wait; +#else + wait_queue_entry_t wait; +#endif + struct sk_buff *skb; + ENTER(); + bt_activate_thread(thread); + init_waitqueue_entry(&wait, current); + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&thread->waitQ, &wait); + OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE); + if (priv->adapter->WakeupTries || + ((!priv->adapter->IntCounter) && + (!priv->bt_dev.tx_dnld_rdy || + skb_queue_empty(&priv->adapter->tx_queue)) + )) { + PRINTM(INFO, "Main: Thread sleeping...\n"); + schedule(); + } + OS_SET_THREAD_STATE(TASK_RUNNING); + remove_wait_queue(&thread->waitQ, &wait); + if (kthread_should_stop() || adapter->SurpriseRemoved) { + PRINTM(INFO, "main-thread: break from main thread: " + "SurpriseRemoved=0x%x\n", + adapter->SurpriseRemoved); + break; + } + + PRINTM(INFO, "Main: Thread waking up...\n"); + + if (priv->adapter->IntCounter) { + OS_INT_DISABLE; + adapter->IntCounter = 0; + OS_INT_RESTORE; + sbi_get_int_status(priv); + } else if ((priv->adapter->ps_state == PS_SLEEP) && + (!skb_queue_empty(&priv->adapter->tx_queue) + )) { + priv->adapter->WakeupTries++; + sbi_wakeup_firmware(priv); + continue; + } + if (priv->adapter->ps_state == PS_SLEEP) + continue; + if (priv->bt_dev.tx_dnld_rdy == TRUE) { + if (!skb_queue_empty(&priv->adapter->tx_queue)) { + skb = skb_dequeue(&priv->adapter->tx_queue); + if (skb) + send_single_packet(priv, skb); + } + } + } + bt_deactivate_thread(thread); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param m_dev A pointer to m_dev structure + * @return N/A + */ +void +bt_interrupt(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + if (!priv || !priv->adapter) { + LEAVE(); + return; + } + PRINTM(INTR, "*\n"); + priv->adapter->ps_state = PS_AWAKE; + if (priv->adapter->hs_state == HS_ACTIVATED) { + PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name); + priv->adapter->hs_state = HS_DEACTIVATED; + } + priv->adapter->WakeupTries = 0; + priv->adapter->IntCounter++; + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); +} + +/** + * @brief Dynamic release of bt private + * + * @param kobj A pointer to kobject structure + * + * @return N/A + */ +static void +bt_private_dynamic_release(struct kobject *kobj) +{ + bt_private *priv = container_of(kobj, bt_private, kobj); + ENTER(); + PRINTM(INFO, "free bt priv\n"); + kfree(priv); + LEAVE(); +} + +static struct kobj_type ktype_bt_private_dynamic = { + .release = bt_private_dynamic_release, +}; + +/** + * @brief Allocation of bt private + * + * @param N/A + * + * @return bt_private + */ +static bt_private * +bt_alloc_priv(void) +{ + bt_private *priv; + ENTER(); + priv = kzalloc(sizeof(bt_private), GFP_KERNEL); + if (priv) { + kobject_init(&priv->kobj, &ktype_bt_private_dynamic); + PRINTM(INFO, "alloc bt priv\n"); + } + LEAVE(); + return priv; +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return kobject structure + */ +struct kobject * +bt_priv_get(bt_private *priv) +{ + PRINTM(INFO, "bt priv get object"); + return kobject_get(&priv->kobj); +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return N/A + */ +void +bt_priv_put(bt_private *priv) +{ + PRINTM(INFO, "bt priv put object"); + kobject_put(&priv->kobj); +} + +/** + * @brief This function send init commands to firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_init_cmd(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + if (ret < 0) { + PRINTM(FATAL, "Module cfg command send failed!\n"); + goto done; + } + if (btindrst != -1) { + ret = bt_set_independent_reset(priv); + if (ret < 0) { + PRINTM(FATAL, "Independent reset failed!\n"); + goto done; + } + } + if (btpmic + && ((priv->card_type == CARD_TYPE_SD8997) || + (priv->card_type == CARD_TYPE_SD8977) || + (priv->card_type == CARD_TYPE_SD8978)) + ) { + if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) { + PRINTM(FATAL, "BT: PMIC Configure failed \n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE); + if (ret < 0) { + PRINTM(FATAL, "%s BLE deepsleep failed!\n", + deep_sleep ? "Enable" : "Disable"); + goto done; + } + if (psmode) { + priv->bt_dev.psmode = TRUE; + priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME; + ret = bt_enable_ps(priv); + if (ret < 0) { + PRINTM(FATAL, "Enable PS mode failed!\n"); + goto done; + } + } +#if defined(SDIO_SUSPEND_RESUME) + priv->bt_dev.gpio_gap = DEF_GPIO_GAP; + ret = bt_send_hscfg_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "Send HSCFG failed!\n"); + goto done; + } +#endif + priv->bt_dev.sdio_pull_cfg = 0xffffffff; + priv->bt_dev.sdio_pull_ctrl = 0; + wake_up_interruptible(&priv->MainThread.waitQ); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reinit firmware after redownload firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_reinit_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + /* block all the packet from bluez */ + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) + priv->adapter->tx_lock = TRUE; + + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) { + priv->adapter->tx_lock = FALSE; + bt_restore_tx_queue(priv); + } + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); +done: + return ret; +} + +/** + * @brief Module configuration and register device + * + * @param priv A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_conf_dpc(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct hci_dev *hdev = NULL; + unsigned char dev_type = 0; + + ENTER(); + + priv->bt_dev.tx_dnld_rdy = TRUE; + if (priv->fw_reload) { + bt_reinit_fw(priv); + LEAVE(); + return ret; + } + + if (drv_mode & DRV_MODE_BT) { + hdev = hci_alloc_dev(); + if (!hdev) { + PRINTM(FATAL, "Can not allocate HCI device\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + hdev->open = bt_open; + hdev->close = bt_close; + hdev->flush = bt_flush; + hdev->send = bt_send_frame; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + hdev->destruct = bt_destruct; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hdev->ioctl = bt_ioctl; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + hci_set_drvdata(hdev, priv); +#else + hdev->driver_data = priv; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + hdev->owner = THIS_MODULE; +#endif + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE; + priv->bt_dev.m_dev[BT_SEQ].spec_type = BLUEZ_SPEC; + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)hdev; + priv->bt_dev.m_dev[BT_SEQ].driver_data = priv; + priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0; + } + + dev_type = HCI_SDIO; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + if (hdev) + hdev->bus = dev_type; +#else + if (hdev) + hdev->type = dev_type; +#endif /* >= 2.6.34 */ + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + + /** Process device tree init parameters before register hci device. + * Since uplayer device has not yet registered, no need to block tx queue. + * */ + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } else if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + /* Get FW version */ + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); + + if (hdev) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + hdev->dev_type = priv->bt_dev.devType; +#endif + ret = hci_register_dev(hdev); + if (ret < 0) { + PRINTM(FATAL, "Can not register HCI device\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + snprintf((char *)priv->bt_dev.m_dev[BT_SEQ].name, + sizeof(priv->bt_dev.m_dev[BT_SEQ].name), hdev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ); + } + +done: + LEAVE(); + return ret; +err_kmalloc: + LEAVE(); + return ret; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the bt_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to bt_private structure + */ + +bt_private * +bt_add_card(void *card) +{ + bt_private *priv = NULL; + int index = 0; + + ENTER(); + + priv = bt_alloc_priv(); + if (!priv) { + PRINTM(FATAL, "Can not allocate priv\n"); + LEAVE(); + return NULL; + } + /* Save the handle */ + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == NULL) + break; + } + if (index < MAX_BT_ADAPTER) { + m_priv[index] = priv; + } else { + PRINTM(ERROR, "Exceeded maximum cards supported!\n"); + goto err_kmalloc; + } + /* allocate buffer for bt_adapter */ + priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL); + if (!priv->adapter) { + PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n"); + goto err_kmalloc; + } + priv->adapter->tx_buffer = + kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->tx_buffer) { + PRINTM(FATAL, "Allocate buffer for transmit\n"); + goto err_kmalloc; + } + priv->adapter->tx_buf = + (u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT); + priv->adapter->hw_regs_buf = + kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->hw_regs_buf) { + PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n"); + goto err_kmalloc; + } + priv->adapter->hw_regs = + (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT); + bt_init_adapter(priv); + + PRINTM(INFO, "Starting kthread...\n"); + priv->MainThread.priv = priv; + spin_lock_init(&priv->driver_lock); + + bt_create_thread(bt_service_main_thread, &priv->MainThread, + "bt_main_service"); + + /* wait for mainthread to up */ + while (!priv->MainThread.pid) + os_sched_timeout(1); + + sdio_update_card_type(priv, card); + /* Update driver version */ + if (priv->card_type == CARD_TYPE_SD8787) + memcpy(mbt_driver_version, CARD_SD8787, strlen(CARD_SD8787)); + else if (priv->card_type == CARD_TYPE_SD8777) + memcpy(mbt_driver_version, CARD_SD8777, strlen(CARD_SD8777)); + else if (priv->card_type == CARD_TYPE_SD8887) + memcpy(mbt_driver_version, CARD_SD8887, strlen(CARD_SD8887)); + else if (priv->card_type == CARD_TYPE_SD8897) + memcpy(mbt_driver_version, CARD_SD8897, strlen(CARD_SD8897)); + else if (priv->card_type == CARD_TYPE_SD8797) + memcpy(mbt_driver_version, CARD_SD8797, strlen(CARD_SD8797)); + else if (priv->card_type == CARD_TYPE_SD8977) + memcpy(mbt_driver_version, CARD_SD8977, strlen(CARD_SD8977)); + else if (priv->card_type == CARD_TYPE_SD8978) + memcpy(mbt_driver_version, CARD_SD8978, strlen(CARD_SD8978)); + else if (priv->card_type == CARD_TYPE_SD8997) + memcpy(mbt_driver_version, CARD_SD8997, strlen(CARD_SD8997)); + else if (priv->card_type == CARD_TYPE_SD8987) + memcpy(mbt_driver_version, CARD_SD8987, strlen(CARD_SD8987)); + + if (BT_STATUS_SUCCESS != sdio_get_sdio_device(priv)) + goto err_kmalloc; + + /** user config file */ + init_waitqueue_head(&priv->init_user_conf_wait_q); + + priv->bt_dev.card = card; + + ((struct sdio_mmc_card *)card)->priv = priv; + priv->adapter->sd_ireg = 0; + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (sbi_register_dev(priv) < 0) { + PRINTM(FATAL, "Failed to register bt device!\n"); + goto err_registerdev; + } + if (bt_init_fw(priv)) { + PRINTM(FATAL, "BT Firmware Init Failed\n"); + goto err_init_fw; + } + LEAVE(); + return priv; + +err_init_fw: + clean_up_m_devs(priv); + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); +err_registerdev: + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); +err_kmalloc: + if (priv->adapter) + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return NULL; +} + +/** + * @brief This function send hardware remove event + * + * @param priv A pointer to bt_private + * @return N/A + */ +void +bt_send_hw_remove_event(bt_private *priv) +{ + struct sk_buff *skb = NULL; + struct hci_dev *hdev = NULL; + ENTER(); + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC) + hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer; +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_HARDWARE_REMOVE 0x24 + skb = bt_skb_alloc(3, GFP_ATOMIC); + skb->data[0] = HCI_HARDWARE_ERROR_EVT; + skb->data[1] = 1; + skb->data[2] = HCI_HARDWARE_REMOVE; + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 3); + if (hdev) { + skb->dev = (void *)hdev; + PRINTM(MSG, "Send HW ERROR event\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + os_sched_timeout(5); + hdev->stat.byte_rx += 3; + } + LEAVE(); + return; +} + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * @return BT_STATUS_SUCCESS + */ +int +bt_remove_card(void *card) +{ + bt_private *priv = (bt_private *)card; + int index; + ENTER(); + if (!priv) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv->adapter->SurpriseRemoved = TRUE; + + bt_send_hw_remove_event(priv); + wake_up_interruptible(&priv->adapter->cmd_wait_q); + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) { + os_sched_timeout(1); + wake_up_interruptible(&priv->MainThread.waitQ); + } + + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); + clean_up_m_devs(priv); + PRINTM(INFO, "Free Adapter\n"); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function initializes module. + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_module(void) +{ + int ret = BT_STATUS_SUCCESS; + int index; + ENTER(); + PRINTM(MSG, "BT: Loading driver\n"); + /* Init the bt_private pointer array first */ + for (index = 0; index < MAX_BT_ADAPTER; index++) + m_priv[index] = NULL; + bt_root_proc_init(); + + /** create char device class */ + chardev_class = class_create(THIS_MODULE, MODULE_NAME); + if (IS_ERR(chardev_class)) { + PRINTM(ERROR, "Unable to allocate class\n"); + bt_root_proc_remove(); + ret = PTR_ERR(chardev_class); + goto done; + } + + if (sbi_register() == NULL) { + bt_root_proc_remove(); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + if (ret) + PRINTM(MSG, "BT: Driver loading failed\n"); + else + PRINTM(MSG, "BT: Driver loaded successfully\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void +bt_exit_module(void) +{ + bt_private *priv; + int index; + ENTER(); + PRINTM(MSG, "BT: Unloading driver\n"); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + priv = m_priv[index]; + if (!priv) + continue; + if (priv && !priv->adapter->SurpriseRemoved) { + if (BT_STATUS_SUCCESS == bt_send_reset_command(priv)) + bt_send_module_cfg_cmd(priv, + MODULE_SHUTDOWN_REQ); + } + sbi_disable_host_int(priv); + + } + + sbi_unregister(); + + bt_root_proc_remove(); + class_destroy(chardev_class); + PRINTM(MSG, "BT: Driver unloaded\n"); + LEAVE(); +} + +module_init(bt_init_module); +module_exit(bt_exit_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +module_param(fw, int, 0); +MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware"); +module_param(fw_crc_check, int, 0); +MODULE_PARM_DESC(fw_crc_check, + "1: Enable FW download CRC check (default); 0: Disable FW download CRC check"); +module_param(psmode, int, 0); +MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode"); +module_param(deep_sleep, int, 0); +MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep"); +#ifdef CONFIG_OF +module_param(dts_enable, int, 0); +MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS"); +#endif +#ifdef DEBUG_LEVEL1 +module_param(mbt_drvdbg, uint, 0); +MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL"); +#endif +#ifdef SDIO_SUSPEND_RESUME +module_param(mbt_pm_keep_power, int, 0); +MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power"); +#endif +module_param(init_cfg, charp, 0); +MODULE_PARM_DESC(init_cfg, "BT init config file name"); +module_param(cal_cfg, charp, 0); +MODULE_PARM_DESC(cal_cfg, "BT calibrate file name"); +module_param(cal_cfg_ext, charp, 0); +MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name"); +module_param(bt_mac, charp, 0660); +MODULE_PARM_DESC(bt_mac, "BT init mac address"); +module_param(drv_mode, int, 0); +MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;"); +module_param(bt_fw_reload, int, 0); +MODULE_PARM_DESC(bt_fw_reload, + "0: disable fw_reload; 1: enable fw reload feature"); +module_param(btindrst, int, 0); +MODULE_PARM_DESC(btindrst, + "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset."); +module_param(btpmic, int, 0); +MODULE_PARM_DESC(btpmic, + "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)"); +module_param(bt_fw_serial, int, 0); +MODULE_PARM_DESC(bt_fw_serial, + "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8987/bt/bt_proc.c b/bt_sd8987/bt/bt_proc.c new file mode 100644 index 0000000..0264c94 --- /dev/null +++ b/bt_sd8987/bt/bt_proc.c
@@ -0,0 +1,723 @@ +/** @file bt_proc.c + * + * @brief This file handle the functions for proc files + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/proc_fs.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** proc diretory root */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#define PROC_DIR NULL +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#define PROC_DIR (&proc_root) +#else +#define PROC_DIR proc_net +#endif + +/** Proc mbt directory entry */ +static struct proc_dir_entry *proc_mbt; + +#define CMD52_STR_LEN 50 +static char cmd52_string[CMD52_STR_LEN]; + +/** proc data structure */ +struct proc_data { + /** Read length */ + int rdlen; + /** Read buffer */ + char *rdbuf; + /** Write length */ + int wrlen; + /** Maximum write length */ + int maxwrlen; + /** Write buffer */ + char *wrbuf; + /** Private structure */ + struct _bt_private *pbt; + void (*on_close) (struct inode *, struct file *); +}; + +/** Default file permission */ +#define DEFAULT_FILE_PERM 0644 + +/** Bluetooth device offset */ +#define OFFSET_BT_DEV 0x01 +/** Bluetooth adapter offset */ +#define OFFSET_BT_ADAPTER 0x02 +/** Show integer */ +#define SHOW_INT 0x10 +/** Show hex */ +#define SHOW_HEX 0x20 +/** Show string */ +#define SHOW_STRING 0x40 + +/** Device size */ +#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n) +/** Device address */ +#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n) + +/** Adapter size */ +#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n) +/** Adapter address */ +#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n) + +static struct item_data config_items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX} + , +#endif + {"idle_timeout", item_dev_size(idle_timeout), 0, + item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX} + , + {"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap), + OFFSET_BT_DEV | SHOW_HEX} + , + {"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0, + item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX} + , + {"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0, + item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT} + , + {"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode), + OFFSET_BT_DEV | SHOW_INT} + , + +}; + +static struct item_data status_items[] = { + {"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver), + OFFSET_BT_ADAPTER | SHOW_STRING}, + {"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0, + item_dev_addr(tx_dnld_rdy), + OFFSET_BT_DEV | SHOW_INT}, + {"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_state", item_adapter_size(hs_state), 0, + item_adapter_addr(hs_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"ps_state", item_adapter_size(ps_state), 0, + item_adapter_addr(ps_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"WakeupTries", item_adapter_size(WakeupTries), 0, + item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_recv", item_adapter_size(irq_recv), 0, + item_adapter_addr(irq_recv), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_done", item_adapter_size(irq_done), 0, + item_adapter_addr(irq_done), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"skb_pending", item_adapter_size(skb_pending), 0, + item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT}, +}; + +static struct item_data debug_items[] = { + {"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING}, +}; + +/** + * @brief convert string to number + * + * @param s pointer to numbered string + * @return converted number from string s + */ +int +string_to_number(char *s) +{ + int r = 0; + int base = 0; + int pn = 1; + + if (strncmp(s, "-", 1) == 0) { + pn = -1; + s++; + } + if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) { + base = 16; + s += 2; + } else + base = 10; + + for (s = s; *s != 0; s++) { + if ((*s >= '0') && (*s <= '9')) + r = (r * base) + (*s - '0'); + else if ((*s >= 'A') && (*s <= 'F')) + r = (r * base) + (*s - 'A' + 10); + else if ((*s >= 'a') && (*s <= 'f')) + r = (r * base) + (*s - 'a' + 10); + else + break; + } + + return r * pn; +} + +/** + * @brief Create cmd52 string + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +static int +form_cmd52_string(bt_private *priv) +{ + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X", + priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg, + priv->bt_dev.cmd52_val); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/* + * @brief Parse cmd52 string + * + * @param buffer A pointer user buffer + * @param len Length of user buffer + * @param func Parsed func number + * @param reg Parsed reg value + * @param val Parsed value to set + * @return BT_STATUS_SUCCESS + */ +static int +parse_cmd52_string(const char __user * buffer, size_t len, + int *func, int *reg, int *val) +{ + int ret = BT_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + + ENTER(); + + string = kzalloc(CMD52_STR_LEN, GFP_KERNEL); + if (!string) { + PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n"); + LEAVE(); + return -ENOMEM; + } + memcpy(string, buffer + strlen("sdcmd52rw="), + len - strlen("sdcmd52rw=")); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) + *func = string_to_number(pos); + + /* Get reg */ + pos = strsep(&string, " \t"); + if (pos) + *reg = string_to_number(pos); + + /* Get val (optional) */ + pos = strsep(&string, " \t"); + if (pos) + *val = string_to_number(pos); + kfree(string); + LEAVE(); + return ret; +} + +/** + * @brief This function handle generic proc file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS + */ +static int +proc_close(struct inode *inode, struct file *file) +{ + struct proc_data *pdata = file->private_data; + ENTER(); + if (pdata) { + if (pdata->on_close != NULL) + pdata->on_close(inode, file); + kfree(pdata->rdbuf); + kfree(pdata->wrbuf); + kfree(pdata); + } + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handle generic proc file read + * + * @param file A pointer to file structure + * @param buffer A pointer to output buffer + * @param len number of byte to read + * @param offset A pointer to offset of file + * @return number of output data + */ +static ssize_t +proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + if ((!pdata->rdbuf) || (pos < 0)) + return -EINVAL; + if (pos >= pdata->rdlen) + return 0; + if (len > pdata->rdlen - pos) + len = pdata->rdlen - pos; + if (copy_to_user(buffer, pdata->rdbuf + pos, len)) + return -EFAULT; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle generic proc file write + * + * @param file A pointer to file structure + * @param buffer A pointer to input buffer + * @param len number of byte to write + * @param offset A pointer to offset of file + * @return number of input data + */ +static ssize_t +proc_write(struct file *file, + const char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + int func = 0, reg = 0, val = 0; + int config_data = 0; + char *line = NULL; + + if (!pdata->wrbuf || (pos < 0)) + return -EINVAL; + if (pos >= pdata->maxwrlen) + return 0; + if (len > pdata->maxwrlen - pos) + len = pdata->maxwrlen - pos; + if (copy_from_user(pdata->wrbuf + pos, buffer, len)) + return -EFAULT; + if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) { + if (!strncmp + (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) { + line = pdata->wrbuf + pos; + line += strlen("fw_reload") + 1; + config_data = string_to_number(line); + } else + config_data = FW_RELOAD_SDIO_INBAND_RESET; + PRINTM(MSG, "Request fw_reload=%d\n", config_data); + bt_request_fw_reload(pdata->pbt, config_data); + } + if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) { + parse_cmd52_string(pdata->wrbuf + pos, len, &func, ®, &val); + sd_write_cmd52_val(pdata->pbt, func, reg, val); + } + if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) { + bt_dump_sdio_regs(pdata->pbt); + bt_dump_firmware_info_v2(pdata->pbt); + } + + if (pos + len > pdata->wrlen) + pdata->wrlen = len + file->f_pos; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle the generic file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return N/A + */ +static void +proc_on_close(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata = file->private_data; + char *line; + int i; + ENTER(); + if (!pdata->wrlen) + return; + line = pdata->wrbuf; + while (line[0]) { + for (i = 0; i < priv->num_items; i++) { + if (!strncmp + (line, priv->pdata[i].name, + strlen(priv->pdata[i].name))) { + line += strlen(priv->pdata[i].name) + 1; + if (priv->pdata[i].size == 1) + *((u8 *)priv->pdata[i].addr) = + (u8)string_to_number(line); + else if (priv->pdata[i].size == 2) + *((u16 *) priv->pdata[i].addr) = + (u16) string_to_number(line); + else if (priv->pdata[i].size == 4) + *((u32 *)priv->pdata[i].addr) = + (u32)string_to_number(line); + } + } + while (line[0] && line[0] != '\n') + line++; + if (line[0]) + line++; + } + if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd + || priv->pbt->bt_dev.sdio_pull_ctrl + || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) { + bt_prepare_command(priv->pbt); + wake_up_interruptible(&priv->pbt->MainThread.waitQ); + } + LEAVE(); + return; +} + +/** + * @brief This function handle the generic file open + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata; + int i; + char *p; + u32 val = 0; + ENTER(); + priv->pbt->adapter->skb_pending = + skb_queue_len(&priv->pbt->adapter->tx_queue); + file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL); + if (file->private_data == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n"); + LEAVE(); + return -ENOMEM; + } + pdata = (struct proc_data *)file->private_data; + pdata->pbt = priv->pbt; + pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL); + if (pdata->rdbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n"); + kfree(file->private_data); + LEAVE(); + return -ENOMEM; + } + if (priv->fileflag == DEFAULT_FILE_PERM) { + pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL); + if (pdata->wrbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n"); + kfree(pdata->rdbuf); + kfree(file->private_data); + return -ENOMEM; + } + pdata->maxwrlen = priv->bufsize; + pdata->on_close = proc_on_close; + } + p = pdata->rdbuf; + for (i = 0; i < priv->num_items; i++) { + if (priv->pdata[i].size == 1) + val = *((u8 *)priv->pdata[i].addr); + else if (priv->pdata[i].size == 2) + val = *((u16 *) priv->pdata[i].addr); + else if (priv->pdata[i].size == 4) + val = *((u32 *)priv->pdata[i].addr); + if (priv->pdata[i].flag & SHOW_INT) + p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_HEX) + p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_STRING) { + if (!strncmp + (priv->pdata[i].name, "sdcmd52rw", + strlen("sdcmd52rw"))) { + sd_read_cmd52_val(priv->pbt); + form_cmd52_string(priv->pbt); + } + p += sprintf(p, "%s=%s\n", priv->pdata[i].name, + (char *)priv->pdata[i].addr); + } + } + pdata->rdlen = strlen(pdata->rdbuf); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** Proc read ops */ +static const struct file_operations proc_read_ops = { + .read = proc_read, + .open = proc_open, + .release = proc_close +}; + +/** Proc Read-Write ops */ +static const struct file_operations proc_rw_ops = { + .read = proc_read, + .write = proc_write, + .open = proc_open, + .release = proc_close +}; + +static struct proc_private_data proc_files[] = { + {"status", S_IRUGO, 1024, + sizeof(status_items) / sizeof(status_items[0]), + &status_items[0], NULL, &proc_read_ops} + , + {"config", DEFAULT_FILE_PERM, 512, + sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL, + &proc_rw_ops} + , + {"debug", DEFAULT_FILE_PERM, 512, + sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL, + &proc_rw_ops} + , +}; + +/** + * @brief This function initializes proc entry + * + * @param priv A pointer to bt_private structure + * @param m_dev A pointer to struct m_dev + * @param seq Sequence number + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq) +{ + int ret = BT_STATUS_SUCCESS; + struct proc_dir_entry *entry; + int i, j; + + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + if (proc_mbt) { + priv->dev_proc[seq].proc_entry = + proc_mkdir(m_dev->name, proc_mbt); + if (!priv->dev_proc[seq].proc_entry) { + PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name); + ret = BT_STATUS_FAILURE; + goto done; + } + + priv->dev_proc[seq].pfiles = + kmalloc(sizeof(proc_files), GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles) { + PRINTM(ERROR, + "BT: Could not alloc memory for pfile!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files, + sizeof(proc_files)); + priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files); + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) + priv->dev_proc[seq].pfiles[j].pdata = NULL; + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + priv->dev_proc[seq].pfiles[j].pdata = + kmalloc(priv->dev_proc[seq].pfiles[j]. + num_items * sizeof(struct item_data), + GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles[j].pdata) { + PRINTM(ERROR, + "BT: Could not alloc memory for pdata!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata, + (u8 *)proc_files[j].pdata, + priv->dev_proc[seq].pfiles[j].num_items * + sizeof(struct item_data)); + for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items; + i++) { + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_DEV) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)&priv->bt_dev; + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_ADAPTER) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)priv->adapter; + } + priv->dev_proc[seq].pfiles[j].pbt = priv; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + entry = proc_create_data(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq].proc_entry, + proc_files[j].fops, + &priv->dev_proc[seq]. + pfiles[j]); + if (entry == NULL) +#else + entry = create_proc_entry(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq]. + proc_entry); + if (entry) { + entry->data = &priv->dev_proc[seq].pfiles[j]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + entry->owner = THIS_MODULE; +#endif + entry->proc_fops = proc_files[j].fops; + } else +#endif + PRINTM(MSG, "BT: Fail to create proc %s\n", + proc_files[j].name); + } + } +done: + if (ret == BT_STATUS_FAILURE) { + if (priv->dev_proc[seq].proc_entry) { + remove_proc_entry(m_dev->name, proc_mbt); + priv->dev_proc[seq].proc_entry = NULL; + } + if (priv->dev_proc[seq].pfiles) { + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + if (priv->dev_proc[seq].pfiles[j].pdata) { + kfree(priv->dev_proc[seq].pfiles[j]. + pdata); + priv->dev_proc[seq].pfiles[j].pdata = + NULL; + } + } + kfree(priv->dev_proc[seq].pfiles); + priv->dev_proc[seq].pfiles = NULL; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function removes proc interface + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_proc_remove(bt_private *priv) +{ + int j, i; + + ENTER(); + PRINTM(INFO, "BT: Remove Proc Interface\n"); + if (proc_mbt) { + for (i = 0; i < MAX_RADIO_FUNC; i++) { + if (!priv->dev_proc[i].proc_entry) + continue; + for (j = 0; j < ARRAY_SIZE(proc_files); j++) { + remove_proc_entry(proc_files[j].name, + priv->dev_proc[i].proc_entry); + } + remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt); + priv->dev_proc[i].proc_entry = NULL; + + if (priv->dev_proc[i].pfiles) { + for (j = 0; + j < priv->dev_proc[i].num_proc_files; + j++) { + if (priv->dev_proc[i].pfiles[j].pdata) { + kfree(priv->dev_proc[i]. + pfiles[j].pdata); + priv->dev_proc[i].pfiles[j]. + pdata = NULL; + } + } + kfree(priv->dev_proc[i].pfiles); + priv->dev_proc[i].pfiles = NULL; + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function creates proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_root_proc_init(void) +{ + PRINTM(INFO, "BT: Create Proc Interface\n"); + proc_mbt = proc_mkdir("mbt", PROC_DIR); + if (!proc_mbt) { + PRINTM(ERROR, "BT: Cannot create proc interface\n"); + return BT_STATUS_FAILURE; + } + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function removes proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS + */ +int +bt_root_proc_remove(void) +{ + remove_proc_entry("mbt", PROC_DIR); + proc_mbt = NULL; + return BT_STATUS_SUCCESS; +}
diff --git a/bt_sd8987/bt/bt_sdio.h b/bt_sd8987/bt/bt_sdio.h new file mode 100644 index 0000000..3fced8f --- /dev/null +++ b/bt_sd8987/bt/bt_sdio.h
@@ -0,0 +1,435 @@ +/** @file bt_sdio.h + * @brief This file contains SDIO (interface) module + * related macros, enum, and structure. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_SDIO_H_ +#define _BT_SDIO_H_ + +#include <linux/irqreturn.h> + +/** SD8787 card type */ +#define CARD_TYPE_SD8787 0x01 +/** SD8777 card type */ +#define CARD_TYPE_SD8777 0x02 +/** SD8887 card type */ +#define CARD_TYPE_SD8887 0x03 +/** SD8897 card type */ +#define CARD_TYPE_SD8897 0x04 +/** SD8797 card type */ +#define CARD_TYPE_SD8797 0x05 +/** SD8977 card type */ +#define CARD_TYPE_SD8977 0x06 +/** SD8997 card type */ +#define CARD_TYPE_SD8997 0x07 +/** SD8987 card type */ +#define CARD_TYPE_SD8987 0x08 +/** SD8978 card type */ +#define CARD_TYPE_SD8978 0x09 + +/** IRQ return type */ +typedef irqreturn_t IRQ_RET_TYPE; +/** IRQ return */ +#define IRQ_RET (return IRQ_HANDLED) +/** ISR notifier function */ +typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id, + struct pt_regs * reg); + +/** SDIO header length */ +#define SDIO_HEADER_LEN 4 + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */ +#define GPIO_INT_NEW_MODE 255 +/* SD block size can not bigger than 64 due to buf size limit in firmware */ +/** define SD block size for data Tx/Rx */ +#define SD_BLOCK_SIZE 64 +/** define SD block size for firmware download */ +#define SD_BLOCK_SIZE_FW_DL 256 + +/** Number of blocks for firmware transfer */ +#define FIRMWARE_TRANSFER_NBLOCK 2 + +/** Firmware ready */ +#define FIRMWARE_READY 0xfedc + +/* Bus Interface Control Reg 0x07 */ +/** SD BUS width 1 */ +#define SD_BUS_WIDTH_1 0x00 +/** SD BUS width 4 */ +#define SD_BUS_WIDTH_4 0x02 +/** SD BUS width mask */ +#define SD_BUS_WIDTH_MASK 0x03 +/** Asynchronous interrupt mode */ +#define ASYNC_INT_MODE 0x20 + +/** magic register */ +#define CARD_MAGIC_REG 0xF0 +/** magic value */ +#define MAGIC_VAL 0x24 + +/* Host Control Registers */ +/** Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/** Host Control Registers : Host without Command 53 finish host*/ +#define HOST_TO_CARD_EVENT (0x1U << 3) +/** Host Control Registers : Host terminates Command 53 */ +#define HOST_TERM_CMD53 (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Host interrupt RSR */ +#define HOST_INT_RSR_REG 0x01 + +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x02 + +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +#define HOST_INTSTATUS_REG 0x03 +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/ +#define ENABLE_GPIO_1_INT_MODE 0x88 +/** Scratch reg 3 2 : Configure GPIO-1 INT*/ +#define SCRATCH_REG_32 0xEE + +/** Host Control Registers : Host interrupt status */ +#define HOST_INT_STATUS_REG 0x28 +/** Host Control Registers : Upload CRC error */ +#define UP_LD_CRC_ERR (0x1U << 2) +/** Host Control Registers : Upload restart */ +#define UP_LD_RESTART (0x1U << 1) +/** Host Control Registers : Download restart */ +#define DN_LD_RESTART (0x1U << 0) + +/* Card Control Registers */ +/** Card Control Registers : Read SQ base address A0 register */ +#define SQ_READ_BASE_ADDRESS_A0_REG 0x40 +/** Card Control Registers : Read SQ base address A1 register */ +#define SQ_READ_BASE_ADDRESS_A1_REG 0x41 +/** Card Control Registers : Read SQ base address A2 register */ +#define SQ_READ_BASE_ADDRESS_A2_REG 0x42 +/** Card Control Registers : Read SQ base address A3 register */ +#define SQ_READ_BASE_ADDRESS_A3_REG 0x43 +/** Card Control Registers : Read SQ base address B0 register */ +#define SQ_READ_BASE_ADDRESS_B0_REG 0x44 +/** Card Control Registers : Read SQ base address B1 register */ +#define SQ_READ_BASE_ADDRESS_B1_REG 0x45 +/** Card Control Registers : Read SQ base address B2 register */ +#define SQ_READ_BASE_ADDRESS_B2_REG 0x46 +/** Card Control Registers : Read SQ base address B3 register */ +#define SQ_READ_BASE_ADDRESS_B3_REG 0x47 + +/** Card Control Registers : Card status register */ +#define CARD_STATUS_REG 0x30 +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host interrupt mask register */ +#define HOST_INTERRUPT_MASK_REG 0x34 +/** Card Control Registers : Host power interrupt mask */ +#define HOST_POWER_INT_MASK (0x1U << 3) +/** Card Control Registers : Abort card interrupt mask */ +#define ABORT_CARD_INT_MASK (0x1U << 2) +/** Card Control Registers : Upload card interrupt mask */ +#define UP_LD_CARD_INT_MASK (0x1U << 1) +/** Card Control Registers : Download card interrupt mask */ +#define DN_LD_CARD_INT_MASK (0x1U << 0) + +/** Card Control Registers : Card interrupt status register */ +#define CARD_INTERRUPT_STATUS_REG 0x38 +/** Card Control Registers : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** Card Control Registers : Card interrupt RSR register */ +#define CARD_INTERRUPT_RSR_REG 0x3c +/** Card Control Registers : Power up RSR */ +#define POWER_UP_RSR (0x1U << 4) +/** Card Control Registers : Power down RSR */ +#define POWER_DOWN_RSR (0x1U << 3) + +/** Card Control Registers : Debug 0 register */ +#define DEBUG_0_REG 0x70 +/** Card Control Registers : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : Debug 1 register */ +#define DEBUG_1_REG 0x71 +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : Debug 2 register */ +#define DEBUG_2_REG 0x72 +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : Debug 3 register */ +#define DEBUG_3_REG 0x73 +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0x78 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0x79 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0x7A + +/** Firmware status 0 register */ +#define CARD_FW_STATUS0_REG 0x60 +/** Firmware status 1 register */ +#define CARD_FW_STATUS1_REG 0x61 +/** Rx length register */ +#define CARD_RX_LEN_REG 0x62 +/** Rx unit register */ +#define CARD_RX_UNIT_REG 0x63 +/** Card Control Registers : Miscellaneous Configuration Register */ +#define CARD_MISC_CFG_REG 0x6C +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT (0x1U << 4) + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0x68 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0x69 +/** Card Control Registers : Card OCR 3 register */ +#define CARD_OCR_3_REG 0x6A +/** Card Control Registers : Card config register */ +#define CARD_CONFIG_REG 0x6B +/** Card Control Registers : Card revision register */ +#define CARD_REVISION_REG 0x5c +/** Card Control Registers : Command 53 finish G BUS */ +#define CMD53_FINISH_GBUS (0x1U << 1) +/** Card Control Registers : SD negative edge */ +#define SD_NEG_EDGE (0x1U << 0) + +/* Special registers in function 0 of the SDxx card */ +/** Special register in function 0 of the SDxxx card : Scratch 0 */ +#define SCRATCH_0_REG 0x80fe +/** Special register in function 0 of the SDxxx card : Scratch 1 */ +#define SCRATCH_1_REG 0x80ff +/** Host F1 read base 0 */ +#define HOST_F1_RD_BASE_0 0x0040 +/** Host F1 read base 1 */ +#define HOST_F1_RD_BASE_1 0x0041 +/** Host F1 card ready */ +#define HOST_F1_CARD_RDY 0x0020 + +/** Chip Id Register 0 */ +#define CARD_CHIP_ID_0_REG 0x801c +/** Chip Id Register 1 */ +#define CARD_CHIP_ID_1_REG 0x801d + +struct sdio_mmc_card { + /** sdio_func structure pointer */ + struct sdio_func *func; + /** bt_private structure pointer */ + bt_private *priv; +}; + +struct sdio_card_reg { + u8 cfg; + u8 host_int_mask; // HOST_INT_MASK_REG + u8 host_intstatus; // HOST_INTSTATUS_REG + u8 host_int_rsr_reg; // HOST_INT_RSR_REG + u8 card_misc_cfg_reg; // CARD_MISC_CFG_REG + u8 card_status; // CARD_STATUS_REG + u8 sq_read_base_addr_a0; // SQ_READ_BASE_ADDRESS_A0_REG + u8 sq_read_base_addr_a1; // SQ_READ_BASE_ADDRESS_A1_REG + u8 card_revision; // CARD_REVISION_REG + u8 card_fw_status0; // CARD_FW_STATUS0_REG + u8 card_fw_status1; // CARD_FW_STATUS1_REG + u8 card_rx_len; // CARD_RX_LEN_REG + u8 card_rx_unit; // CARD_RX_UNIT_REG + u8 io_port_0; // IO_PORT_0_REG + u8 io_port_1; // IO_PORT_1_REG + u8 io_port_2; // IO_PORT_2_REG +}; + +struct sdio_device { + const struct sdio_card_reg *reg; +}; + +static const struct sdio_card_reg bt_reg_87xx = { + .cfg = 0x00, + .host_int_mask = 0x02, // HOST_INT_MASK_REG + .host_intstatus = 0x03, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x01, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0x6c, // CARD_MISC_CFG_REG + .card_status = 0x30, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x40, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x41, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0x5C, // CARD_REVISION_REG + .card_fw_status0 = 0x60, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0x61, // CARD_FW_STATUS1_REG + .card_rx_len = 0x62, // CARD_RX_LEN_REG + .card_rx_unit = 0x63, // CARD_RX_UNIT_REG + .io_port_0 = 0x78, // IO_PORT_0_REG + .io_port_1 = 0x79, // IO_PORT_1_REG + .io_port_2 = 0x7a, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8887 = { + .cfg = 0x00, + .host_int_mask = 0x08, // HOST_INT_MASK_REG + .host_intstatus = 0x0C, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x04, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xD8, // CARD_MISC_CFG_REG + .card_status = 0x5C, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x6C, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x6D, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xC8, // CARD_REVISION_REG + .card_fw_status0 = 0x88, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0x89, // CARD_FW_STATUS1_REG + .card_rx_len = 0x8A, // CARD_RX_LEN_REG + .card_rx_unit = 0x8B, // CARD_RX_UNIT_REG + .io_port_0 = 0xE4, // IO_PORT_0_REG + .io_port_1 = 0xE5, // IO_PORT_1_REG + .io_port_2 = 0xE6, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8897 = { + .cfg = 0x00, + .host_int_mask = 0x02, // HOST_INT_MASK_REG + .host_intstatus = 0x03, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x01, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xCC, // CARD_MISC_CFG_REG + .card_status = 0x50, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x60, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x61, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xBC, // CARD_REVISION_REG + .card_fw_status0 = 0xC0, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0xC1, // CARD_FW_STATUS1_REG + .card_rx_len = 0xC2, // CARD_RX_LEN_REG + .card_rx_unit = 0xC3, // CARD_RX_UNIT_REG + .io_port_0 = 0xD8, // IO_PORT_0_REG + .io_port_1 = 0xD9, // IO_PORT_1_REG + .io_port_2 = 0xDA, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8977_8978_8997 = { + .cfg = 0x00, + .host_int_mask = 0x08, // HOST_INT_MASK_REG + .host_intstatus = 0x0C, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x04, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xD8, // CARD_MISC_CFG_REG + .card_status = 0x5C, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0xF8, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0xF9, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xC8, // CARD_REVISION_REG + .card_fw_status0 = 0xE8, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0xE9, // CARD_FW_STATUS1_REG + .card_rx_len = 0xEA, // CARD_RX_LEN_REG + .card_rx_unit = 0xEB, // CARD_RX_UNIT_REG + .io_port_0 = 0xE4, // IO_PORT_0_REG + .io_port_1 = 0xE5, // IO_PORT_1_REG + .io_port_2 = 0xE6, // IO_PORT_2_REG +}; + +static const struct sdio_device bt_sdio_sd8787 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8777 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8887 = { + .reg = &bt_reg_8887, +}; + +static const struct sdio_device bt_sdio_sd8897 = { + .reg = &bt_reg_8897, +}; + +static const struct sdio_device bt_sdio_sd8797 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8977 = { + .reg = &bt_reg_8977_8978_8997, +}; + +static const struct sdio_device bt_sdio_sd8978 = { + .reg = &bt_reg_8977_8978_8997, +}; + +static const struct sdio_device bt_sdio_sd8997 = { + .reg = &bt_reg_8977_8978_8997, +}; + +static const struct sdio_device bt_sdio_sd8987 = { + .reg = &bt_reg_8977_8978_8997, +}; + +/** DMA alignment value */ +#define DMA_ALIGNMENT 64 +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** This function read cmd52 register */ +int sd_write_reg(bt_private *priv, int reg, u8 val); +/** This function write cmd52 value to register */ +int sd_read_reg(bt_private *priv, int reg, u8 *data); +/** This function reads the Cmd52 value in dev structure */ +int sd_read_cmd52_val(bt_private *priv); +/** This function updates card reg based on the Cmd52 value in dev structure */ +int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val); + +void sdio_update_card_type(bt_private *priv, void *card); +int sdio_get_sdio_device(bt_private *priv); + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** This function tells lower driver that BT is suspended */ +void bt_is_suspended(bt_private *priv); +#endif +#endif +#endif +#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8987/bt/bt_sdiommc.c b/bt_sd8987/bt/bt_sdiommc.c new file mode 100644 index 0000000..0a6f951 --- /dev/null +++ b/bt_sd8987/bt/bt_sdiommc.c
@@ -0,0 +1,2337 @@ +/** @file bt_sdiommc.c + * @brief This file contains SDIO IF (interface) module + * related functions. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/firmware.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/card.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** define marvell vendor id */ +#define MARVELL_VENDOR_ID 0x02df + +/** Max retry number of CMD53 read/write */ +#define MAX_CMD53_RETRY 3 +/** Max retry number of CMD53 read/write */ +#define MAX_CMD52_RETRY 3 +/** Firmware name */ +static char *fw_name; +/** fw serial download flag */ +extern int bt_fw_serial; +int bt_intmode = INT_MODE_SDIO; +/** request firmware nowait */ +int bt_req_fw_nowait; +static int multi_fn = BIT(2); + +#define DEFAULT_FW_NAME "" + +/** FW header length for CRC check disable */ +#define FW_CRC_HEADER_RB2 28 +/** FW header for CRC check disable */ +static u8 fw_crc_header_rb_2[FW_CRC_HEADER_RB2] = { + 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x9d, 0x32, 0xbb, 0x11, 0x01, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x67, 0xd6, 0xfc, 0x25 +}; + +/** FW header length for CRC check disable */ +#define FW_CRC_HEADER_RB 24 +/** FW header for CRC check disable */ +static u8 fw_crc_header_rb_1[FW_CRC_HEADER_RB] = { + 0x01, 0x00, 0x00, 0x00, 0x04, 0xfd, 0x00, 0x04, + 0x08, 0x00, 0x00, 0x00, 0x26, 0x52, 0x2a, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** Default firmware name */ +#define DEFAULT_FW_NAME_8777 "mrvl/sd8777_uapsta.bin" +#define DEFAULT_FW_NAME_8787 "mrvl/sd8787_uapsta.bin" +#define DEFAULT_FW_NAME_8797 "mrvl/sd8797_uapsta.bin" +#define DEFAULT_FW_NAME_8887 "mrvl/sd8887_uapsta.bin" +#define DEFAULT_FW_NAME_8897 "mrvl/sd8897_uapsta.bin" +#define DEFAULT_FW_NAME_8977 "mrvl/sdsd8977_combo.bin" +#define DEFAULT_FW_NAME_8978 "mrvl/sdsd8978_combo.bin" +#define DEFAULT_FW_NAME_8997 "mrvl/sdsd8997_combo.bin" + +/** SD8787 chip revision ID */ +#define SD8787_W0 0x30 +#define SD8787_W1 0x31 +#define SD8787_A0_A1 0x40 +/** SD8797 chip revision ID */ +#define SD8797_A0 0x00 +#define SD8797_B0 0x10 +/** SD8897 chip revision ID */ +#define SD8897_A0 0x10 +#define SD8897_B0 0x20 + +/** SD8887 chip revision ID */ +#define SD8887_A0 0x0 +#define SD8887_A2 0x2 +#define SD8887_A0_FW_NAME "mrvl/sd8887_uapsta.bin" +#define SD8887_A2_FW_NAME "mrvl/sd8887_uapsta_a2.bin" +#define SD8887_A2_BT_FW_NAME "mrvl/sd8887_bt_a2.bin" + +#define SD8897_A0_FW_NAME "mrvl/sd8897_uapsta_a0.bin" +#define SD8897_B0_FW_NAME "mrvl/sd8897_uapsta.bin" + +#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin" +#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin" +#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin" +#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin" + +/** SD8977 chip revision ID */ +#define SD8977_V0 0x0 +#define SD8977_V1 0x8 +#define SD8977_V2 0x9 +#define SD8977_V0_FW_NAME "mrvl/sdsd8977_combo.bin" +#define SD8977_V0_BT_FW_NAME "mrvl/sd8977_bt.bin" +#define SD8977_V1_FW_NAME "mrvl/sdsd8977_combo_v1.bin" +#define SD8977_V1_BT_FW_NAME "mrvl/sd8977_bt_v1.bin" +#define SD8977_V2_FW_NAME "mrvl/sdsd8977_combo_v2.bin" +#define SD8977_V2_BT_FW_NAME "mrvl/sd8977_bt_v2.bin" +/** SD8978 FW NAME */ +#define SD8978_FW_NAME "mrvl/sdsd8978_combo.bin" +#define SD8978_BT_FW_NAME "mrvl/sd8978_bt.bin" + +/** SD8997 chip revision ID */ +#define SD8997_Z 0x02 +#define SD8997_V2 0x10 +#define SD8997_Z_FW_NAME "mrvl/sdsd8997_combo.bin" +#define SD8997_Z_BT_FW_NAME "mrvl/sd8997_bt.bin" +#define SD8997_V2_FW_NAME "mrvl/sdsd8997_combo_v2.bin" +#define SD8997_V2_BT_FW_NAME "mrvl/sd8997_bt_v2.bin" +#define SD8997_V3_FW_NAME "mrvl/sdsd8997_combo_v3.bin" +#define SD8997_V3_BT_FW_NAME "mrvl/sd8997_bt_v3.bin" + +/** SD8987 */ +#define SD8987_FW_NAME "mrvl/sdsd8987_combo.bin" +#define SD8987_BT_FW_NAME "mrvl/sd8987_bt.bin" + +/** Function number 2 */ +#define FN2 2 +/** Device ID for SD8787 FN2 */ +#define SD_DEVICE_ID_8787_BT_FN2 0x911A +/** Device ID for SD8787 FN3 */ +#define SD_DEVICE_ID_8787_BT_FN3 0x911B +/** Device ID for SD8777 FN2 */ +#define SD_DEVICE_ID_8777_BT_FN2 0x9132 +/** Device ID for SD8777 FN3 */ +#define SD_DEVICE_ID_8777_BT_FN3 0x9133 +/** Device ID for SD8887 FN2 */ +#define SD_DEVICE_ID_8887_BT_FN2 0x9136 +/** Device ID for SD8887 FN3 */ +#define SD_DEVICE_ID_8887_BT_FN3 0x9137 +/** Device ID for SD8897 FN2 */ +#define SD_DEVICE_ID_8897_BT_FN2 0x912E +/** Device ID for SD8897 FN3 */ +#define SD_DEVICE_ID_8897_BT_FN3 0x912F +/** Device ID for SD8797 FN2 */ +#define SD_DEVICE_ID_8797_BT_FN2 0x912A +/** Device ID for SD8797 FN3 */ +#define SD_DEVICE_ID_8797_BT_FN3 0x912B +/** Device ID for SD8977 FN2 */ +#define SD_DEVICE_ID_8977_BT_FN2 0x9146 +/** Device ID for SD8978 FN2 */ +#define SD_DEVICE_ID_8978_BT_FN2 0x915a +/** Device ID for SD8997 FN2 */ +#define SD_DEVICE_ID_8997_BT_FN2 0x9142 +/** Device ID for SD8987 FN2 */ +#define SD_DEVICE_ID_8987_BT_FN2 0x914a +/** Device ID for SD8987 FN3 */ +#define SD_DEVICE_ID_8987_BT_FN3 0x914b + +/** Array of SDIO device ids when multi_fn=0x12 */ +static const struct sdio_device_id bt_ids[] = { + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8978_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8987_BT_FN2)}, + {} +}; + +MODULE_DEVICE_TABLE(sdio, bt_ids); + +#ifdef SDIO_OOB_IRQ +extern int mrvl_sdio_claim_irq(struct sdio_func *func, + sdio_irq_handler_t * handler); +extern int mrvl_sdio_release_irq(struct sdio_func *func); +extern int mrvl_sdio_suspend(struct sdio_func *func); +extern int mrvl_sdio_resume(struct sdio_func *func); +#endif + +/******************************************************** + Global Variables +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +extern int mbt_pm_keep_power; +#endif + +extern bt_private *m_priv[]; +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function gets rx_unit value + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_get_rx_unit(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_unit_reg = priv->psdio_device->reg->card_rx_unit; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_unit_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + priv->bt_dev.rx_unit = reg; + + LEAVE(); + return ret; +} + +/** + * @brief This function reads fwstatus registers + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_read_firmware_status(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 fws0; + u8 fws1; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0; + u8 card_fw_status1_reg = priv->psdio_device->reg->card_fw_status1; + + ENTER(); + + fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + *dat = (((u16) fws1) << 8) | fws0; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function reads rx length + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sd_read_rx_len(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_len_reg = priv->psdio_device->reg->card_rx_len; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_len_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + *dat = (u16) reg << priv->bt_dev.rx_unit; + + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts mask + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_enable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask; + + ENTER(); + + sdio_writeb(card->func, mask, host_int_mask_reg, &ret); + if (ret) { + PRINTM(WARN, "BT: Unable to enable the host interrupt!\n"); + ret = BT_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** @brief This function disables the host interrupts mask. + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sbi_disable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_FAILURE; + u8 host_int_mask; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask; + + ENTER(); + + /* Read back the host_int_mask register */ + host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret); + if (ret) + goto done; + + /* Update with the mask and write back to the register */ + host_int_mask &= ~mask; + sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret); + if (ret < 0) { + PRINTM(WARN, "BT: Unable to diable the host interrupt!\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function polls the card status register + * + * @param priv A pointer to bt_private structure + * @param bits the bit mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_poll_card_status(bt_private *priv, u8 bits) +{ + int tries; + int rval; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 cs; + u8 card_status_reg = priv->psdio_device->reg->card_status; + + ENTER(); + + for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { + cs = sdio_readb(card->func, card_status_reg, &rval); + if (rval != 0) + break; + if (rval == 0 && (cs & bits) == bits) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + udelay(1); + } + PRINTM(ERROR, + "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n", + rval, tries, cs); + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_cmd52_val(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 func, reg, val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + func = priv->bt_dev.cmd52_func; + reg = priv->bt_dev.cmd52_reg; + sdio_claim_host(card->func); + if (func) + val = sdio_readb(card->func, reg, &ret); + else + val = sdio_f0_readb(card->func, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n", + func, reg); + } else { + priv->bt_dev.cmd52_val = val; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param func Stores func variable + * @param reg Stores reg variable + * @param val Stores val variable + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_cmd52_val(bt_private *priv, int func, int reg, int val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + if (val >= 0) { + /* Perform actual write only if val is provided */ + sdio_claim_host(card->func); + if (func) + sdio_writeb(card->func, val, reg, &ret); + else + sdio_f0_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, + "BT: Cannot write value (0x%x) to func %d reg %d\n", + val, func, reg); + goto done; + } + priv->bt_dev.cmd52_val = val; + } + + /* Save current func and reg for future read */ + priv->bt_dev.cmd52_func = func; + priv->bt_dev.cmd52_reg = reg; + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to write + * @param val value + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_reg(bt_private *priv, int reg, u8 val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + sdio_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to read + * @param data Data + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_reg(bt_private *priv, int reg, u8 *data) +{ + int ret = BT_STATUS_SUCCESS; + u8 val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + *data = val; + LEAVE(); + return ret; +} + +/** + * @brief This function probes the card + * + * @param func A pointer to sdio_func structure. + * @param id A pointer to structure sdio_device_id + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = NULL; + struct sdio_mmc_card *card = NULL; + + ENTER(); + + PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor, + id->device, id->class, func->num); + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto done; + } + card->func = func; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + /* wait for chip fully wake up */ + if (!func->enable_timeout) + func->enable_timeout = 200; +#endif + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) { + sdio_disable_func(func); + sdio_release_host(func); + PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret); + kfree(card); + LEAVE(); + return -EIO; + } + sdio_release_host(func); + priv = bt_add_card(card); + if (!priv) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + ret = BT_STATUS_FAILURE; + kfree(card); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks if the firmware is ready to accept + * command or not. + * + * @param priv A pointer to bt_private structure + * @param pollnum Number of times to poll fw status + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_verify_fw_download(bt_private *priv, int pollnum) +{ + int ret = BT_STATUS_FAILURE; + u16 firmwarestat = 0; + int tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if (sd_read_firmware_status(priv, &firmwarestat) < 0) + continue; + if (firmwarestat == FIRMWARE_READY) { + PRINTM(MSG, "BT FW is active(%d)\n", tries); + ret = BT_STATUS_SUCCESS; + break; + } + mdelay(100); + } + if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) { + PRINTM(ERROR, + "Fail to poll firmware status: firmwarestat=0x%x\n", + firmwarestat); + bt_dump_sdio_regs(priv); + } + LEAVE(); + return ret; +} + +/** + * @brief Transfers firmware to card + * + * @param priv A Pointer to bt_private structure + * @param fw A Pointer to fw image + * @param fw_len fw image len + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len) +{ + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 *firmware = fw; + int firmwarelen = fw_len; + u8 base0; + u8 base1; + int ret = BT_STATUS_SUCCESS; + int offset; + void *tmpfwbuf = NULL; + int tmpfwbufsz; + u8 *fwbuf; + u16 len; + int txlen = 0; + int tx_blocks = 0; + int i = 0; + int tries = 0; +#ifdef FW_DOWNLOAD_SPEED + u32 tv1, tv2; +#endif + u8 sq_read_base_address_a0_reg = + priv->psdio_device->reg->sq_read_base_addr_a0; + u8 sq_read_base_address_a1_reg = + priv->psdio_device->reg->sq_read_base_addr_a1; + u8 crc_buffer = 0; + u8 *header_crc_fw = NULL; + u8 header_crc_fw_len = 0; + + if (priv->card_type == CARD_TYPE_SD8787) { + header_crc_fw = fw_crc_header_rb_1; + header_crc_fw_len = FW_CRC_HEADER_RB; + } else if (priv->card_type == CARD_TYPE_SD8777) { + header_crc_fw = fw_crc_header_rb_2; + header_crc_fw_len = FW_CRC_HEADER_RB2; + } + + ENTER(); + + PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen); + +#ifdef FW_DOWNLOAD_SPEED + tv1 = get_utimeofday(); +#endif + + tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT; + tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); + if (!tmpfwbuf) { + PRINTM(ERROR, + "BT: Unable to allocate buffer for firmware. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + /* Ensure aligned firmware buffer */ + fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); + + if (!(priv->fw_crc_check) + && ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + ) { + /* CRC check not required, use custom header first */ + firmware = header_crc_fw; + firmwarelen = header_crc_fw_len; + crc_buffer = 1; + } + + /* Perform firmware data transfer */ + offset = 0; + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits + */ + ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret < 0) { + PRINTM(FATAL, + "BT: FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + if (!crc_buffer) + /* More data? */ + if (offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + base0 = sdio_readb(card->func, + sq_read_base_address_a0_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", + base0, base0); + ret = BT_STATUS_FAILURE; + goto done; + } + base1 = sdio_readb(card->func, + sq_read_base_address_a1_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download\n", + base1, base1); + ret = BT_STATUS_FAILURE; + goto done; + } + len = (((u16) base1) << 8) | base0; + + if (len != 0) + break; + udelay(10); + } + + if (len == 0) + break; + else if (len > BT_UPLD_SIZE) { + PRINTM(FATAL, + "BT: FW download failure @ %d, invalid length %d\n", + offset, len); + ret = BT_STATUS_FAILURE; + goto done; + } + /** ignore CRC check before download the first packet */ + if (offset == 0 && (len & BIT(0))) + len &= ~BIT(0); + txlen = len; + + if (len & BIT(0)) { + i++; + if (i >= MAX_CMD53_RETRY) { + PRINTM(FATAL, + "BT: FW download failure @ %d, over max retry count\n", + offset); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: FW CRC error indicated by the helper:" + " len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + + PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last block */ + if (firmwarelen - offset < txlen) + txlen = firmwarelen - offset; + + PRINTM(INFO, "."); + + tx_blocks = + (txlen + SD_BLOCK_SIZE_FW_DL - + 1) / SD_BLOCK_SIZE_FW_DL; + + /* Copy payload to buffer */ + memcpy(fwbuf, &firmware[offset], txlen); + } + + /* Send data */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf, + tx_blocks * SD_BLOCK_SIZE_FW_DL); + + if (ret < 0) { + PRINTM(ERROR, + "BT: FW download, write iomem (%d) failed @ %d\n", + i, offset); + sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret); + if (ret) + PRINTM(ERROR, "write ioreg failed (CFG)\n"); + } + + offset += txlen; + if (crc_buffer + && ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + ) { + if (offset >= header_crc_fw_len) { + /* Custom header download complete, restore + original FW */ + offset = 0; + firmware = fw; + firmwarelen = fw_len; + crc_buffer = 0; + } + } + } while (TRUE); + + PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset); + + ret = BT_STATUS_SUCCESS; +done: +#ifdef FW_DOWNLOAD_SPEED + tv2 = get_utimeofday(); + PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000, + (tv1 % 1000000) / 1000, tv1 % 1000); + PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); + tv2 -= tv1; + PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); +#endif + kfree(tmpfwbuf); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * + * @param fw_firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_request_fw_dpc(const struct firmware *fw_firmware, void *context) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = (bt_private *)context; + struct sdio_mmc_card *card = NULL; + struct m_dev *m_dev_bt = NULL; + struct timeval tstamp; + int index; + + ENTER(); + + m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ]; + + if ((priv == NULL) || (priv->adapter == NULL) || + (priv->bt_dev.card == NULL) || (m_dev_bt == NULL) + ) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + card = (struct sdio_mmc_card *)priv->bt_dev.card; + + if (!fw_firmware) { + do_gettimeofday(&tstamp); + if (tstamp.tv_sec > + (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(ERROR, + "BT: No firmware image found. Skipping download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: No firmware image found! Retrying download\n"); + /* Wait a second here before calling the callback again */ + os_sched_timeout(1000); + sd_download_firmware_w_helper(priv); + LEAVE(); + return ret; + } + + priv->firmware = fw_firmware; + + if (BT_STATUS_FAILURE == + sd_init_fw_dpc(priv, (u8 *)priv->firmware->data, + priv->firmware->size)) { + PRINTM(ERROR, + "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n", + bt_req_fw_nowait); + sdio_release_host(card->func); + ret = BT_STATUS_FAILURE; + goto done; + } + + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) { + PRINTM(ERROR, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + sdio_release_host(card->func); + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + LEAVE(); + return ret; + +done: + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + /* For synchronous download cleanup will be done in add_card */ + if (!bt_req_fw_nowait) + return ret; + PRINTM(INFO, "unregister device\n"); + sbi_unregister_dev(priv); + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); + bt_proc_remove(priv); + clean_up_m_devs(priv); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return None + **/ +static void +sd_request_fw_callback(const struct firmware *firmware, void *context) +{ + ENTER(); + sd_request_fw_dpc(firmware, context); + LEAVE(); + return; +} + +/** + * @brief This function downloads firmware image to the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sd_download_firmware_w_helper(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + int err; + char *cur_fw_name = NULL; + + ENTER(); + + cur_fw_name = fw_name; + + if (fw_name == NULL) { + if (priv->card_type == CARD_TYPE_SD8787) + cur_fw_name = DEFAULT_FW_NAME_8787; + else if (priv->card_type == CARD_TYPE_SD8777) + cur_fw_name = DEFAULT_FW_NAME_8777; + else if (priv->card_type == CARD_TYPE_SD8887) { + /* Check revision ID */ + switch (priv->adapter->chip_rev) { + case SD8887_A0: + cur_fw_name = SD8887_A0_FW_NAME; + break; + case SD8887_A2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8887_A2_FW_NAME; + else + cur_fw_name = SD8887_A2_BT_FW_NAME; + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8887; + break; + } + } else if (priv->card_type == CARD_TYPE_SD8897) + cur_fw_name = DEFAULT_FW_NAME_8897; + else if (priv->card_type == CARD_TYPE_SD8797) + cur_fw_name = DEFAULT_FW_NAME_8797; + else if (priv->card_type == CARD_TYPE_SD8977) { + switch (priv->adapter->chip_rev) { + case SD8977_V0: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V0_FW_NAME; + else + cur_fw_name = SD8977_V0_BT_FW_NAME; + break; + case SD8977_V1: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V1_FW_NAME; + else + cur_fw_name = SD8977_V1_BT_FW_NAME; + break; + case SD8977_V2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V2_FW_NAME; + else + cur_fw_name = SD8977_V2_BT_FW_NAME; + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8977; + break; + } + } else if (priv->card_type == CARD_TYPE_SD8978) { + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8978_FW_NAME; + else + cur_fw_name = SD8978_BT_FW_NAME; + } else if (priv->card_type == CARD_TYPE_SD8997) + switch (priv->adapter->chip_rev) { + case SD8997_Z: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8997_Z_FW_NAME; + else + cur_fw_name = SD8997_Z_BT_FW_NAME; + break; + case SD8997_V2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) { + if (priv->adapter->magic_val == + MAGIC_VAL) + cur_fw_name = SD8997_V3_FW_NAME; + else + cur_fw_name = SD8997_V2_FW_NAME; + } else { + if (priv->adapter->magic_val == + MAGIC_VAL) + cur_fw_name = + SD8997_V3_BT_FW_NAME; + else + cur_fw_name = + SD8997_V2_BT_FW_NAME; + } + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8997; + break; + } else if (priv->card_type == CARD_TYPE_SD8987) { + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8987_FW_NAME; + else + cur_fw_name = SD8987_BT_FW_NAME; + } + } + + PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + GFP_KERNEL, priv, + sd_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#endif +#endif + if (ret < 0) + PRINTM(FATAL, + "BT: request_firmware_nowait() failed, error code = %#x\n", + ret); + } else { + err = request_firmware(&priv->firmware, cur_fw_name, + priv->hotplug_device); + if (err < 0) { + PRINTM(FATAL, + "BT: request_firmware() failed, error code = %#x\n", + err); + ret = BT_STATUS_FAILURE; + } else + ret = sd_request_fw_dpc(priv->firmware, priv); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function reads data from the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_card_to_host(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 buf_len = 0; + int buf_block_len; + int blksz; + struct sk_buff *skb = NULL; + u32 type; + u8 *payload = NULL; + struct hci_dev *hdev = NULL; + struct sdio_mmc_card *card = priv->bt_dev.card; + int i = 0; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC) + hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer; + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + do { + /* Read the length of data to be transferred */ + ret = sd_read_rx_len(priv, &buf_len); + if (ret < 0) { + i++; + PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i); + if (i >= MAX_CMD52_RETRY) { + ret = BT_STATUS_FAILURE; + goto exit; + } + udelay(20); + } + } + while (ret == BT_STATUS_FAILURE); + + /* Allocate buffer */ + blksz = SD_BLOCK_SIZE; + buf_block_len = (buf_len + blksz - 1) / blksz; + if (buf_len <= BT_HEADER_LEN || + (buf_block_len * blksz) > ALLOC_BUF_SIZE) { + PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n", + buf_len); + ret = BT_STATUS_FAILURE; + goto exit; + } + skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + goto exit; + } + if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) { + skb_put(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + skb_pull(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + } + + payload = skb->data; + i = 0; + do { + ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: card_to_host, read iomem (%d) failed: %d\n", + i, ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) { + kfree_skb(skb); + skb = NULL; + goto exit; + } + } + } while (ret == BT_STATUS_FAILURE); + /* This is SDIO specific header length: byte[2][1][0], * type: byte[3] + (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ + buf_len = payload[0]; + buf_len |= (u16) payload[1] << 8; + type = payload[3]; + PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", hdev->name, + buf_len, type); + if (buf_len > buf_block_len * blksz) { + PRINTM(ERROR, + "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n", + buf_len, buf_block_len * blksz); + ret = BT_STATUS_FAILURE; + kfree_skb(skb); + skb = NULL; + goto exit; + } + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len); + switch (type) { + case HCI_ACLDATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (*(u16 *) skb->data == 0xffff) { + bt_store_firmware_dump(priv, skb->data, skb->len); + dev_kfree_skb_any(skb); + break; + } + bt_recv_frame(priv, skb); + break; + case HCI_SCODATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + bt_recv_frame(priv, skb); + break; + case HCI_EVENT_PKT: + /** add EVT Demux */ + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb)) + break; + switch (skb->data[0]) { + case 0x0E: + /** cmd complete */ + bt_recv_frame(priv, skb); + break; + case 0x0F: + /** cmd status */ + bt_recv_frame(priv, skb); + break; + case 0xFF: + /** Vendor specific pkt */ + bt_recv_frame(priv, skb); + break; + default: + bt_recv_frame(priv, skb); + break; + } + break; + case MRVL_VENDOR_PKT: + /* Just think here need to back compatible FM */ + bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) + bt_recv_frame(priv, skb); + break; + default: + /* Driver specified event and command resp should be handle + here */ + PRINTM(INFO, "BT: Unknown PKT type:%d\n", type); + kfree_skb(skb); + skb = NULL; + break; + } +exit: + if (ret) { + if (hdev) + hdev->stat.err_rx++; + PRINTM(ERROR, "error when recv pkt!\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function removes the card + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_remove_card(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + card = sdio_get_drvdata(func); + if (card) { + bt_remove_card(card->priv); + kfree(card); + } + } + + LEAVE(); +} + +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_interrupt(struct sdio_func *func) +{ + bt_private *priv; + struct m_dev *m_dev = NULL; + struct sdio_mmc_card *card; + int ret = BT_STATUS_SUCCESS; + u8 ireg = 0; + u8 host_intstatus_reg = 0; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->priv) { + PRINTM(INFO, + "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", + __func__, func, card); + LEAVE(); + return; + } + priv = card->priv; + host_intstatus_reg = priv->psdio_device->reg->host_intstatus; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + if (priv->card_type == CARD_TYPE_SD8887 || + priv->card_type == CARD_TYPE_SD8897 || + priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987 || + priv->card_type == CARD_TYPE_SD8978) { + ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0, + SD_BLOCK_SIZE); + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n", + ret); + goto done; + } + ireg = priv->adapter->hw_regs[host_intstatus_reg]; + } else { + ireg = sdio_readb(card->func, host_intstatus_reg, &ret); + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n", + ret); + goto done; + } + } + if (ireg != 0) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * Clear the interrupt status register and re-enable + * the interrupt + */ + PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name, + ireg); + priv->adapter->irq_recv = ireg; + if (priv->card_type == CARD_TYPE_SD8777 || + priv->card_type == CARD_TYPE_SD8787) { + sdio_writeb(card->func, + ~(ireg) & (DN_LD_HOST_INT_STATUS | + UP_LD_HOST_INT_STATUS), + host_intstatus_reg, &ret); + if (ret) { + PRINTM(ERROR, + "BT: sdio_write_ioreg: clear int status register failed\n"); + goto done; + } + } + } else { + PRINTM(ERROR, "BT: ERR: ireg=0\n"); + } + OS_INT_DISABLE; + priv->adapter->sd_ireg |= ireg; + OS_INT_RESTORE; + bt_interrupt(m_dev); +done: + LEAVE(); +} + +/** + * @brief This function checks if the interface is ready to download + * or not while other download interfaces are present + * + * @param priv A pointer to bt_private structure + * @param val Winner status (0: winner) + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_check_winner_status(bt_private *priv, u8 *val) +{ + + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0; + + ENTER(); + winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret); + if (ret != BT_STATUS_SUCCESS) { + LEAVE(); + return BT_STATUS_FAILURE; + } + *val = winner; + + LEAVE(); + return ret; +} + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** @brief This function tells lower driver that BT is suspended + * + * @param priv A pointer to bt_private structure + * @return None + */ +void +bt_is_suspended(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + priv->adapter->is_suspended = TRUE; + sdio_func_suspended(card->func); +} +#endif + +/** @brief This function handles client driver suspend + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +bt_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + struct hci_dev *hcidev; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name); + hcidev = (struct hci_dev *)m_dev->dev_pointer; + hci_suspend_dev(hcidev); + skb_queue_purge(&priv->adapter->tx_queue); + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) { + PRINTM(CMD, "BT: HS not actived, suspend fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to suspend!\n"); + } + } + } + + priv->adapter->is_suspended = TRUE; + +#ifdef SDIO_OOB_IRQ + mrvl_sdio_suspend(func); +#endif + LEAVE(); + /* We will keep the power when hs enabled successfully */ + if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) { +#ifdef MMC_PM_SKIP_RESUME_PROBE + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and " + "MMC_PM_SKIP_RESUME_PROBE\n"); + return sdio_set_host_pm_flags(func, + MMC_PM_KEEP_POWER | + MMC_PM_SKIP_RESUME_PROBE); +#else + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n"); + return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +#endif + } else { + PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n"); + return BT_STATUS_SUCCESS; + } +} + +void +bt_sdio_shutdown(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is shutdown\n", + sdio_func_id(func)); + return; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return; + } + + priv = cardp->priv; + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) { + PRINTM(CMD, "BT: HS not actived, shutdown fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to shutdown!\n"); + } + } + } + LEAVE(); +} + +/** @brief This function handles client driver resume + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS + */ +int +bt_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + struct hci_dev *hcidev; + + ENTER(); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + priv->adapter->is_suspended = FALSE; +#ifdef SDIO_OOB_IRQ + mrvl_sdio_resume(func); +#endif + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name); + hcidev = (struct hci_dev *)m_dev->dev_pointer; + hci_resume_dev(hcidev); + sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name); + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif +#endif + +/******************************************************** + Global Functions +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +static const struct dev_pm_ops bt_sdio_pm_ops = { + .suspend = bt_sdio_suspend, + .resume = bt_sdio_resume, +}; +#endif +#endif +static struct sdio_driver sdio_bt = { + .name = "sdio_bt", + .id_table = bt_ids, + .probe = sd_probe_card, + .remove = sd_remove_card, +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &bt_sdio_pm_ops, + .shutdown = bt_sdio_shutdown, + } +#endif +#endif +}; + +/** + * @brief This function registers the bt module in bus driver. + * + * @return An int pointer that keeps returned value + */ +int * +sbi_register(void) +{ + int *ret; + + ENTER(); + + if (sdio_register_driver(&sdio_bt) != 0) { + PRINTM(FATAL, "BT: SD Driver Registration Failed\n"); + LEAVE(); + return NULL; + } else + ret = (int *)1; + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the bt module in bus driver. + * + * @return N/A + */ +void +sbi_unregister(void) +{ + ENTER(); + sdio_unregister_driver(&sdio_bt); + LEAVE(); +} + +/** + * @brief This function registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_dev(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + u8 chiprev; + struct sdio_mmc_card *card = priv->bt_dev.card; + struct sdio_func *func; + u8 host_intstatus_reg = priv->psdio_device->reg->host_intstatus; + u8 host_int_rsr_reg = priv->psdio_device->reg->host_int_rsr_reg; + u8 card_misc_cfg_reg = priv->psdio_device->reg->card_misc_cfg_reg; + u8 card_revision_reg = priv->psdio_device->reg->card_revision; + u8 io_port_0_reg = priv->psdio_device->reg->io_port_0; + u8 io_port_1_reg = priv->psdio_device->reg->io_port_1; + u8 io_port_2_reg = priv->psdio_device->reg->io_port_2; + u8 card_magic_reg = CARD_MAGIC_REG; + u8 magic_val = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: Error: card or function is NULL!\n"); + goto failed; + } + func = card->func; + priv->hotplug_device = &func->dev; + + /* Initialize the private structure */ + strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name)); + priv->bt_dev.ioport = 0; + priv->bt_dev.fn = func->num; + + sdio_claim_host(func); +#ifdef SDIO_OOB_IRQ + ret = mrvl_sdio_claim_irq(func, sd_interrupt); +#else + ret = sdio_claim_irq(func, sd_interrupt); +#endif + if (ret) { + PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret); + goto release_host; + } + ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); + if (ret) { + PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__); + goto release_irq; + } + + /* read Revision Register to get the chip revision number */ + chiprev = sdio_readb(func, card_revision_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n"); + goto release_irq; + } + priv->adapter->chip_rev = chiprev; + PRINTM(INFO, "revision=%#x\n", chiprev); + + magic_val = sdio_readb(func, card_magic_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n"); + goto release_irq; + } + priv->adapter->magic_val = magic_val; + PRINTM(INFO, "magic_val=%#x\n", magic_val); + + /* + * Read the HOST_INTSTATUS_REG for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + reg = sdio_readb(func, host_intstatus_reg, &ret); + if (ret < 0) + goto release_irq; + + /* Read the IO port */ + reg = sdio_readb(func, io_port_0_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= reg; + + reg = sdio_readb(func, io_port_1_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 8); + + reg = sdio_readb(func, io_port_2_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 16); + + PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn, + priv->bt_dev.ioport); + + if (priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8978) { + if (bt_intmode == INT_MODE_GPIO) { + PRINTM(MSG, "Enable GPIO-1 INT\n"); + sdio_writeb(func, ENABLE_GPIO_1_INT_MODE, + SCRATCH_REG_32, &ret); + if (ret < 0) + goto release_irq; + } + } + +#define SDIO_INT_MASK 0x3F + if (priv->card_type == CARD_TYPE_SD8887 || + priv->card_type == CARD_TYPE_SD8897 || + priv->card_type == CARD_TYPE_SD8797 || + priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987 || + priv->card_type == CARD_TYPE_SD8978) { + /* Set Host interrupt reset to read to clear */ + reg = sdio_readb(func, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + /* Set auto re-enable */ + reg = sdio_readb(func, card_misc_cfg_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg, + &ret); + if (ret < 0) + goto release_irq; + } + + sdio_set_drvdata(func, card); + sdio_release_host(func); + + LEAVE(); + return BT_STATUS_SUCCESS; +release_irq: +#ifdef SDIO_OOB_IRQ + mrvl_sdio_release_irq(func); +#else + sdio_release_irq(func); +#endif +release_host: + sdio_release_host(func); +failed: + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function de-registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_unregister_dev(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + if (card && card->func) { + sdio_claim_host(card->func); +#ifdef SDIO_OOB_IRQ + mrvl_sdio_release_irq(card->func); +#else + sdio_release_irq(card->func); +#endif + sdio_disable_func(card->func); + sdio_release_host(card->func); + sdio_set_drvdata(card->func, NULL); + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_enable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sd_enable_host_int_mask(priv, HIM_ENABLE); + sd_get_rx_unit(priv); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_disable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sbi_disable_host_int_mask(priv, HIM_DISABLE); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param priv A pointer to bt_private structure + * @param payload A pointer to the data/cmd buffer + * @param nb Length of data/cmd + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + int ret = BT_STATUS_SUCCESS; + int buf_block_len; + int blksz; + int i = 0; + u8 *buf = NULL; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + buf = payload; + + blksz = SD_BLOCK_SIZE; + buf_block_len = (nb + blksz - 1) / blksz; + /* Allocate buffer and copy payload */ + if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) { + if (nb > MAX_TX_BUF_SIZE) { + PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb); + LEAVE(); + return BT_STATUS_FAILURE; + } + /* Ensure 8-byte aligned CMD buffer */ + buf = priv->adapter->tx_buf; + memcpy(buf, payload, nb); + } + sdio_claim_host(card->func); + do { + /* Transfer data to card */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: host_to_card, write iomem (%d) failed: %d\n", + i, ret); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) + goto exit; + } else { + PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n", + m_dev->name, nb); + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb); + } + } while (ret == BT_STATUS_FAILURE); + priv->bt_dev.tx_dnld_rdy = FALSE; +exit: + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function downloads firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_download_fw(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + sdio_claim_host(card->func); + if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) { + PRINTM(MSG, "BT: FW already downloaded!\n"); + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + /* Check if other interface is downloading */ + ret = sd_check_winner_status(priv, &winner); + if (ret == BT_STATUS_FAILURE) { + PRINTM(FATAL, "BT read winner status failed!\n"); + goto done; + } + if (winner) { + PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n", + winner); + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) { + PRINTM(FATAL, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + + do_gettimeofday(&priv->req_fw_time); + /* Download the main firmware via the helper firmware */ + if (sd_download_firmware_w_helper(priv)) { + PRINTM(INFO, "BT: FW download failed!\n"); + ret = BT_STATUS_FAILURE; + } + goto exit; +done: + sdio_release_host(card->func); +exit: + LEAVE(); + return ret; +err_register: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the interrupt status and handle it accordingly. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_get_int_status(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 sdio_ireg = 0; + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + OS_INT_DISABLE; + sdio_ireg = priv->adapter->sd_ireg; + priv->adapter->sd_ireg = 0; + OS_INT_RESTORE; + sdio_claim_host(card->func); + priv->adapter->irq_done = sdio_ireg; + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ + if (priv->bt_dev.tx_dnld_rdy) { /* tx_done already received */ + PRINTM(INFO, + "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", + priv->bt_dev.tx_dnld_rdy, sdio_ireg); + } else { + priv->bt_dev.tx_dnld_rdy = TRUE; + } + } + if (sdio_ireg & UP_LD_HOST_INT_STATUS) + sd_card_to_host(priv); + + ret = BT_STATUS_SUCCESS; + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function wakeup firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_wakeup_firmware(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + sdio_release_host(card->func); + PRINTM(CMD, "BT wake up firmware\n"); + + LEAVE(); + return ret; +} + +/** @brief This function updates the SDIO card types + * + * @param priv A Pointer to the bt_private structure + * @param card A Pointer to card + * + * @return N/A + */ +void +sdio_update_card_type(bt_private *priv, void *card) +{ + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card; + + /* Update card type */ + if (cardp->func->device == SD_DEVICE_ID_8777_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8777_BT_FN3) + priv->card_type = CARD_TYPE_SD8777; + else if (cardp->func->device == SD_DEVICE_ID_8787_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8787_BT_FN3) + priv->card_type = CARD_TYPE_SD8787; + else if (cardp->func->device == SD_DEVICE_ID_8887_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8887_BT_FN3) + priv->card_type = CARD_TYPE_SD8887; + else if (cardp->func->device == SD_DEVICE_ID_8897_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8897_BT_FN3) + priv->card_type = CARD_TYPE_SD8897; + else if (cardp->func->device == SD_DEVICE_ID_8797_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8797_BT_FN3) + priv->card_type = CARD_TYPE_SD8797; + else if (cardp->func->device == SD_DEVICE_ID_8977_BT_FN2) + priv->card_type = CARD_TYPE_SD8977; + else if (cardp->func->device == SD_DEVICE_ID_8978_BT_FN2) + priv->card_type = CARD_TYPE_SD8978; + else if (cardp->func->device == SD_DEVICE_ID_8997_BT_FN2) + priv->card_type = CARD_TYPE_SD8997; + else if (cardp->func->device == SD_DEVICE_ID_8987_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8987_BT_FN3) + priv->card_type = CARD_TYPE_SD8987; +} + +/** + * @brief This function get sdio device from card type + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +sdio_get_sdio_device(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 card_type = priv->card_type; + + ENTER(); + + switch (card_type) { + case CARD_TYPE_SD8777: + priv->psdio_device = &bt_sdio_sd8777; + break; + case CARD_TYPE_SD8787: + priv->psdio_device = &bt_sdio_sd8787; + break; + case CARD_TYPE_SD8887: + priv->psdio_device = &bt_sdio_sd8887; + break; + case CARD_TYPE_SD8897: + priv->psdio_device = &bt_sdio_sd8897; + break; + case CARD_TYPE_SD8797: + priv->psdio_device = &bt_sdio_sd8797; + break; + case CARD_TYPE_SD8977: + priv->psdio_device = &bt_sdio_sd8977; + break; + case CARD_TYPE_SD8978: + priv->psdio_device = &bt_sdio_sd8978; + break; + case CARD_TYPE_SD8997: + priv->psdio_device = &bt_sdio_sd8997; + break; + case CARD_TYPE_SD8987: + priv->psdio_device = &bt_sdio_sd8987; + break; + default: + PRINTM(ERROR, "BT can't get right card type \n"); + ret = BT_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +#define SD8897_INIT_START_REG 0xDC +#define SD8897_INIT_END_REG 0xE1 +#define SD8887_INIT_START_REG 0xA0 +#define SD8887_INIT_END_REG 0xA5 +#define SD8977_SD8978_SD8997_INIT_START_REG 0xF1 +#define SD8977_SD8978_SD8997_INIT_END_REG 0xF6 + +/** @brief This function dump the SDIO register + * + * @param priv A Pointer to the bt_private structure + * + * @return N/A + */ +void +bt_dump_sdio_regs(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + char buf[256], *ptr; + u8 loop, func, data; + unsigned int reg, reg_start, reg_end; + u8 index = 0; + unsigned int reg_table_8887[] = { 0x58, 0x59, 0x5c, 0x60, 0x64, 0x70, + 0x71, 0x72, 0x73, 0xd8, 0xd9, 0xda + }; + u8 loop_num = 0; + unsigned int *reg_table = NULL; + u8 reg_table_size = 0; + unsigned int init_reg_start = 0; + unsigned int init_reg_end = 0; + if (priv->card_type == CARD_TYPE_SD8887) { + init_reg_start = SD8887_INIT_START_REG; + init_reg_end = SD8887_INIT_END_REG; + } else if (priv->card_type == CARD_TYPE_SD8897) { + init_reg_start = SD8897_INIT_START_REG; + init_reg_end = SD8897_INIT_END_REG; + } else if (priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987 || + priv->card_type == CARD_TYPE_SD8978) { + init_reg_start = SD8977_SD8978_SD8997_INIT_START_REG; + init_reg_end = SD8977_SD8978_SD8997_INIT_END_REG; + } + + if (priv->card_type == CARD_TYPE_SD8887) { + loop_num = 3; + reg_table = reg_table_8887; + reg_table_size = sizeof(reg_table_8887) / sizeof(int); + } else + loop_num = 2; + if (priv->adapter->ps_state) + sbi_wakeup_firmware(priv); + + sdio_claim_host(card->func); + for (loop = 0; loop < loop_num; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else if (loop == 2) { + /* Read specific registers of SDIO function1 */ + index = 0; + func = 2; + reg_start = reg_table[index++]; + reg_end = reg_table[reg_table_size - 1]; + } else { + func = 2; + reg_start = 0; + reg_end = 0x09; + } + if (loop == 2) + ptr += sprintf(ptr, "SDIO Func%d: ", func); + else + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, + reg_start, reg_end); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(card->func, reg, &ret); + else + data = sdio_readb(card->func, reg, &ret); + if (loop == 2) + ptr += sprintf(ptr, "(%#x)", reg); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + if (loop == 2 && reg < reg_end) + reg = reg_table[index++]; + else + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + + if (init_reg_start) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ", + init_reg_start, init_reg_end); + for (reg = init_reg_start; reg <= init_reg_end;) { + data = sdio_readb(card->func, reg, &ret); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + sdio_release_host(card->func); +} + +module_param(fw_name, charp, 0); +MODULE_PARM_DESC(fw_name, "Firmware name"); +module_param(bt_req_fw_nowait, int, 0); +MODULE_PARM_DESC(bt_req_fw_nowait, + "0: Use request_firmware API; 1: Use request_firmware_nowait API"); +module_param(multi_fn, int, 0); +MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;"); + +module_param(bt_intmode, int, 0); +MODULE_PARM_DESC(bt_intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO");
diff --git a/bt_sd8987/bt/hci_wrapper.h b/bt_sd8987/bt/hci_wrapper.h new file mode 100644 index 0000000..c3c5035 --- /dev/null +++ b/bt_sd8987/bt/hci_wrapper.h
@@ -0,0 +1,163 @@ +/** @file hci_wrapper.h + * @brief This file contains HCI related definitions + * + * Copyright (C) 2011-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _HCI_WRAPPER_H_ +#define _HCI_WRAPPER_H_ + +#include <linux/module.h> +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +/** Define Seq num */ +#define BT_SEQ 0 + +/** Define dev type */ +#define BT_TYPE 1 +#define BT_AMP_TYPE 2 + +/** Define spec type */ +#define BLUEZ_SPEC 1 +#define IANYWHERE_SPEC 2 +#define GENERIC_SPEC 3 + +/** Define lock/unlock wrapper */ +#define mdev_req_lock(d) down(&d->req_lock) +#define mdev_req_unlock(d) up(&d->req_lock) + +/** Length of device name */ +#define DEV_NAME_LEN 32 + +/** Define struct m_dev */ +struct m_dev { + char name[DEV_NAME_LEN]; + int index; + unsigned long flags; + spinlock_t lock; + struct semaphore req_lock; + struct sk_buff_head rx_q; + wait_queue_head_t req_wait_q; + struct hci_dev_stats stat; + struct module *owner; + void *dev_pointer; + int dev_type; + int spec_type; + void *driver_data; + int read_continue_flag; + int wait_rx_complete; + int rx_complete_flag; + wait_queue_head_t rx_wait_q; + spinlock_t rxlock; + atomic_t extra_cnt; + + struct sk_buff *evt_skb; + struct sk_buff *acl_skb; + struct sk_buff *sco_skb; + + int (*open) (struct m_dev * m_dev); + int (*close) (struct m_dev * m_dev); + int (*flush) (struct m_dev * m_dev); + int (*send) (struct m_dev * m_dev, struct sk_buff * skb); + void (*destruct) (struct m_dev * m_dev); + void (*notify) (struct m_dev * m_dev, unsigned int evt); + int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg); + void (*query) (struct m_dev * m_dev, void *arg); + +}; + +/** Define struct mbt_dev */ +struct mbt_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; + __u8 type; + + __u16 pkt_type; + __u16 esco_type; + __u16 link_policy; + __u16 link_mode; + + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + + struct sk_buff *reassembly[3]; + + atomic_t promisc; +}; + +/** This function frees m_dev allocation */ +void free_m_dev(struct m_dev *m_dev); + +/** + * @brief This function receives frames + * + * @param skb A pointer to struct sk_buff + * @return 0--success otherwise error code + */ +static inline int +mdev_recv_frame(struct sk_buff *skb) +{ + struct m_dev *m_dev = (struct m_dev *)skb->dev; + if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags) + && !test_bit(HCI_INIT, &m_dev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Incomming skb */ + bt_cb(skb)->incoming = 1; + + /* Time stamp */ + __net_timestamp(skb); + + /* Queue frame for rx task */ + skb_queue_tail(&m_dev->rx_q, skb); + + /* Wakeup rx thread */ + wake_up_interruptible(&m_dev->req_wait_q); + + return 0; +} + +/** + * @brief mbt dev suspend handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_suspend_dev(struct m_dev *m_dev) +{ + return 0; +} + +/** + * @brief mbt dev resume handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_resume_dev(struct m_dev *m_dev) +{ + return 0; +} + +#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8987/bt/mbt_char.c b/bt_sd8987/bt/mbt_char.c new file mode 100644 index 0000000..f863bc6 --- /dev/null +++ b/bt_sd8987/bt/mbt_char.c
@@ -0,0 +1,799 @@ +/** @file mbt_char.c + * + * @brief This file contains the char device function calls + * + * Copyright (C) 2010-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/path.h> +#include <linux/namei.h> +#include <linux/mount.h> + +#include "bt_drv.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include <linux/sched/signal.h> +#endif +#include "mbt_char.h" + +static LIST_HEAD(char_dev_list); + +static DEFINE_SPINLOCK(char_dev_list_lock); + +static int mbtchar_major = MBTCHAR_MAJOR_NUM; + +/** + * @brief Gets char device structure + * + * @param dev A pointer to char_dev + * + * @return kobject structure + */ +struct kobject * +chardev_get(struct char_dev *dev) +{ + struct kobject *kobj; + + kobj = bt_priv_get(dev->m_dev->driver_data); + if (!kobj) + return NULL; + PRINTM(INFO, "dev get kobj\n"); + kobj = kobject_get(&dev->kobj); + if (!kobj) + bt_priv_put(dev->m_dev->driver_data); + return kobj; +} + +/** + * @brief Prints char device structure + * + * @param dev A pointer to char_dev + * + * @return N/A + */ +void +chardev_put(struct char_dev *dev) +{ + if (dev) { + struct m_dev *m_dev = dev->m_dev; + PRINTM(INFO, "dev put kobj\n"); + kobject_put(&dev->kobj); + if (m_dev) + bt_priv_put(m_dev->driver_data); + } +} + +/** + * @brief Changes permissions of the dev + * + * @param name pointer to character + * @param mode mode_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chmod(char *name, mode_t mode) +{ + struct path path; + struct inode *inode; + struct iattr newattrs; + int ret; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chmod(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief Changes ownership of the dev + * + * @param name pointer to character + * @param user uid_t type data + * @param group gid_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chown(char *name, uid_t user, gid_t group) +{ + struct path path; + struct inode *inode = NULL; + struct iattr newattrs; + int ret = 0; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chown(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_valid = ATTR_CTIME; + if (user != (uid_t) (-1)) { + newattrs.ia_valid |= ATTR_UID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_uid = user; +#else + newattrs.ia_uid = KUIDT_INIT(user); +#endif + } + if (group != (gid_t) (-1)) { + newattrs.ia_valid |= ATTR_GID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_gid = group; +#else + newattrs.ia_gid = KGIDT_INIT(group); +#endif + } + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief write handler for char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes written + */ +ssize_t +chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos) +{ + int nwrite = 0; + struct sk_buff *skb; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + if (!test_bit(HCI_UP, &m_dev->flags)) { + LEAVE(); + return -EBUSY; + } + nwrite = count; + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n"); + LEAVE(); + return -ENOMEM; + } + + if (copy_from_user((void *)skb_put(skb, count), buf, count)) { + PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n"); + kfree_skb(skb); + nwrite = -EFAULT; + goto exit; + } + + skb->dev = (void *)m_dev; + bt_cb(skb)->pkt_type = *((unsigned char *)skb->data); + skb_pull(skb, 1); + + PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len); + + /* Send skb to the hci wrapper layer */ + if (m_dev->send(m_dev, skb)) { + PRINTM(ERROR, "Write: Fail\n"); + nwrite = 0; + /* Send failed */ + kfree_skb(skb); + } +exit: + LEAVE(); + return nwrite; +} + +/** + * @brief read handler for BT char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes read + */ +ssize_t +chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos) +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + /* Wait for rx data */ + add_wait_queue(&m_dev->req_wait_q, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + skb = skb_dequeue(&m_dev->rx_q); + if (skb) + break; + if (!test_bit(HCI_UP, &m_dev->flags)) { + ret = -EBUSY; + break; + } + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&m_dev->req_wait_q, &wait); + + if (!skb) + goto out; + + if (m_dev->read_continue_flag == 0) { + /* Put type byte before the data */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + } + DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len); + if (skb->len > count) { + /* user data length is smaller than the skb length */ + if (copy_to_user(buf, skb->data, count)) { + ret = -EFAULT; + goto outf; + } + skb_pull(skb, count); + skb_queue_head(&m_dev->rx_q, skb); + m_dev->read_continue_flag = 1; + wake_up_interruptible(&m_dev->req_wait_q); + ret = count; + goto out; + } else { + if (copy_to_user(buf, skb->data, skb->len)) { + ret = -EFAULT; + goto outf; + } + m_dev->read_continue_flag = 0; + ret = skb->len; + } +outf: + kfree_skb(skb); +out: + if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) { + m_dev->rx_complete_flag = TRUE; + wake_up_interruptible(&m_dev->rx_wait_q); + } + LEAVE(); + return ret; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl common handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg) +#else +/** + * @brief ioctl common handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +char_ioctl(struct file *filp, unsigned int cmd, void *arg) +#endif +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { + case MBTCHAR_IOCTL_RELEASE: + m_dev->close(m_dev); + break; + case MBTCHAR_IOCTL_QUERY_TYPE: + m_dev->query(m_dev, arg); + break; + default: + m_dev->ioctl(m_dev, cmd, arg); + break; + } + LEAVE(); + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, (void *)arg); +#else + return char_ioctl(filp, cmd, (void *)arg); +#endif +} + +#ifdef CONFIG_COMPAT +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief compat ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl_compat(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief compat ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, compat_ptr(arg)); +#else + return char_ioctl(filp, cmd, compat_ptr(arg)); +#endif +} +#endif /* CONFIG_COMPAT */ + +/** + * @brief open handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = NULL; + struct m_dev *m_dev = NULL; + struct char_dev *cdev = NULL; + struct list_head *p = NULL; + ENTER(); + + list_for_each(p, &char_dev_list) { + cdev = list_entry(p, struct char_dev, list); + if (mbtchar_major == MAJOR(inode->i_cdev->dev) && + cdev->minor == MINOR(inode->i_cdev->dev)) { + dev = cdev; + break; + } + } + if (!dev) { + PRINTM(ERROR, "cannot find dev from inode\n"); + LEAVE(); + return -ENXIO; + } + if (!chardev_get(dev)) { + LEAVE(); + return -ENXIO; + } + filp->private_data = dev; /* for other methods */ + m_dev = dev->m_dev; + mdev_req_lock(m_dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (test_bit(HCI_UP, &m_dev->flags)) { + atomic_inc(&m_dev->extra_cnt); + goto done; + } +#endif + if (m_dev->open(m_dev)) { + ret = -EIO; + goto done; + } + set_bit(HCI_UP, &m_dev->flags); + +done: + mdev_req_unlock(m_dev); + if (ret) + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief release handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) { + LEAVE(); + return ret; + } +#endif + if (m_dev) + ret = dev->m_dev->close(dev->m_dev); + filp->private_data = NULL; + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief poll handler for char dev + * + * @param filp pointer to structure file + * @param wait pointer to poll_table structure + * @return mask + */ +static unsigned int +chardev_poll(struct file *filp, poll_table * wait) +{ + unsigned int mask; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + + m_dev = dev->m_dev; + poll_wait(filp, &m_dev->req_wait_q, wait); + mask = POLLOUT | POLLWRNORM; + if (skb_peek(&m_dev->rx_q)) + mask |= POLLIN | POLLRDNORM; + if (!test_bit(HCI_UP, &(m_dev->flags))) + mask |= POLLHUP; + PRINTM(INFO, "poll mask=0x%x\n", mask); + LEAVE(); + return mask; +} + +/* File ops for the Char driver */ +const struct file_operations chardev_fops = { + .owner = THIS_MODULE, + .read = chardev_read, + .write = chardev_write, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + .ioctl = chardev_ioctl, +#else + .unlocked_ioctl = chardev_ioctl, +#endif +#ifdef CONFIG_COMPAT + .compat_ioctl = chardev_ioctl_compat, +#endif + .open = chardev_open, + .release = chardev_release, + .poll = chardev_poll, +}; + +/** + * @brief This function creates the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param mod_name A pointer to char + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name) +{ + int ret = 0, dev_num; + unsigned long flags; + ENTER(); + /* create the chrdev region */ + if (mbtchar_major) { + dev_num = MKDEV(mbtchar_major, dev->minor); + ret = register_chrdev_region(dev_num, 1, mod_name); + } else { + PRINTM(INFO, "chardev: no major # yet\n"); + ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1, + mod_name); + } + + if (ret) { + PRINTM(ERROR, "chardev: create chrdev_region failed\n"); + LEAVE(); + return ret; + } + if (!mbtchar_major) { + /* Store the allocated dev major # */ + mbtchar_major = MAJOR(dev_num); + } + dev->cdev = cdev_alloc(); + dev->cdev->ops = &chardev_fops; + dev->cdev->owner = chardev_fops.owner; + dev_num = MKDEV(mbtchar_major, dev->minor); + + if (cdev_add(dev->cdev, dev_num, 1)) { + PRINTM(ERROR, "chardev: cdev_add failed\n"); + ret = -EFAULT; + goto free_cdev_region; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } +#else + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } +#endif + PRINTM(INFO, "register char dev=%s\n", dev_name); + + /** modify later */ + + spin_lock_irqsave(&char_dev_list_lock, flags); + list_add_tail(&dev->list, &char_dev_list); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + + LEAVE(); + return ret; +free_cdev_region: + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + LEAVE(); + return ret; +} + +/** + * @brief This function deletes the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name) +{ + ENTER(); + device_destroy(char_class, MKDEV(mbtchar_major, dev->minor)); + cdev_del(dev->cdev); + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + PRINTM(INFO, "unregister char dev=%s\n", dev_name); + + LEAVE(); + return 0; +} + +/** + * @brief This function cleans module + * + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup(struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + do { + dev = NULL; + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + unregister_char_dev(dev, char_class, dev->m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } while (dev); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + class_destroy(char_class); + LEAVE(); +} + +/** + * @brief This function cleans module + * + * @param m_dev A pointer to m_dev struct + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + if (dev->minor == m_dev->index) { + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + dev->m_dev = NULL; + unregister_char_dev(dev, char_class, m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } + spin_unlock_irqrestore(&char_dev_list_lock, flags); + LEAVE(); +}
diff --git a/bt_sd8987/bt/mbt_char.h b/bt_sd8987/bt/mbt_char.h new file mode 100644 index 0000000..d753601 --- /dev/null +++ b/bt_sd8987/bt/mbt_char.h
@@ -0,0 +1,67 @@ +/** @file mbt_char.h + * + * @brief This file contains mbtchar driver specific defines etc + * + * Copyright (C) 2010-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifndef __MBT_CHAR_H__ +#define __MBT_CHAR_H__ + +#include <linux/cdev.h> +#include <linux/device.h> + +/** Define ioctl */ +#define MBTCHAR_IOCTL_RELEASE _IO('M', 1) +#define MBTCHAR_IOCTL_QUERY_TYPE _IO('M', 2) + +#define MBTCHAR_MAJOR_NUM (0) + +/** Interface specific macros */ +#define FMCHAR_MINOR_BASE (10) +#define NFCCHAR_MINOR_BASE (20) + +/** Declaration of char_dev struct */ +struct char_dev { + struct list_head list; + int minor; + int dev_type; + struct cdev *cdev; + struct m_dev *m_dev; + struct kobject kobj; +}; + +/** Changes permissions of the dev */ +int mbtchar_chmod(char *name, mode_t mode); + +/** Changes ownership of the dev */ +int mbtchar_chown(char *name, uid_t user, gid_t group); + +/** This function creates the char dev */ +int register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name); + +/** This function deletes the char dev */ +int unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name); + +/** This function cleans module */ +void chardev_cleanup(struct class *char_class); + +/** This function cleans module */ +void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class); + +#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8987/bt_char/bt_drv.h b/bt_sd8987/bt_char/bt_drv.h new file mode 100644 index 0000000..2c4c9c6 --- /dev/null +++ b/bt_sd8987/bt_char/bt_drv.h
@@ -0,0 +1,924 @@ +/** @file bt_drv.h + * @brief This header file contains global constant/enum definitions, + * global variable declaration. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_DRV_H_ +#define _BT_DRV_H_ + +#include <linux/version.h> +#include <linux/kthread.h> +#include <linux/skbuff.h> +#include <linux/vmalloc.h> + +#include "hci_wrapper.h" + +/** MAX adapter BT driver supported */ +#define MAX_BT_ADAPTER 3 + +#ifndef BIT +/** BIT definition */ +#define BIT(x) (1UL << (x)) +#endif + +#ifdef MBT_64BIT +typedef u64 t_ptr; +#else +typedef u32 t_ptr; +#endif + +/** max number of adapter supported */ +#define MAX_BT_ADAPTER 3 +/** Define drv_mode bit */ +#define DRV_MODE_BT BIT(0) + +/** Define devFeature bit */ +#define DEV_FEATURE_BT BIT(0) +#define DEV_FEATURE_BTAMP BIT(1) +#define DEV_FEATURE_BLE BIT(2) + +/** Define maximum number of radio func supported */ +#define MAX_RADIO_FUNC 4 + +/** MAC address print format */ +#ifndef MACSTR +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +/** MAC address print arguments */ +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Debug level : Message */ +#define DBG_MSG BIT(0) +/** Debug level : Fatal */ +#define DBG_FATAL BIT(1) +/** Debug level : Error */ +#define DBG_ERROR BIT(2) +/** Debug level : Data */ +#define DBG_DATA BIT(3) +/** Debug level : Command */ +#define DBG_CMD BIT(4) +/** Debug level : Event */ +#define DBG_EVENT BIT(5) +/** Debug level : Interrupt */ +#define DBG_INTR BIT(6) + +/** Debug entry : Data dump */ +#define DBG_DAT_D BIT(16) +/** Debug entry : Data dump */ +#define DBG_CMD_D BIT(17) + +/** Debug level : Entry */ +#define DBG_ENTRY BIT(28) +/** Debug level : Warning */ +#define DBG_WARN BIT(29) +/** Debug level : Informative */ +#define DBG_INFO BIT(30) + +#ifdef DEBUG_LEVEL1 +extern u32 mbt_drvdbg; + +#ifdef DEBUG_LEVEL2 +/** Print informative message */ +#define PRINTM_INFO(msg...) \ + do {if (mbt_drvdbg & DBG_INFO) \ + printk(KERN_DEBUG msg); } while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) \ + do {if (mbt_drvdbg & DBG_WARN) \ + printk(KERN_DEBUG msg); } while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) \ + do {if (mbt_drvdbg & DBG_ENTRY) \ + printk(KERN_DEBUG msg); } while (0) +#else +/** Print informative message */ +#define PRINTM_INFO(msg...) do {} while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) do {} while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +/** Print interrupt message */ +#define PRINTM_INTR(msg...) \ + do {if (mbt_drvdbg & DBG_INTR) \ + printk(KERN_DEBUG msg); } while (0) +/** Print event message */ +#define PRINTM_EVENT(msg...) \ + do {if (mbt_drvdbg & DBG_EVENT) \ + printk(KERN_DEBUG msg); } while (0) +/** Print command message */ +#define PRINTM_CMD(msg...) \ + do {if (mbt_drvdbg & DBG_CMD) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data message */ +#define PRINTM_DATA(msg...) \ + do {if (mbt_drvdbg & DBG_DATA) \ + printk(KERN_DEBUG msg); } while (0) +/** Print error message */ +#define PRINTM_ERROR(msg...) \ + do {if (mbt_drvdbg & DBG_ERROR) \ + printk(KERN_ERR msg); } while (0) +/** Print fatal message */ +#define PRINTM_FATAL(msg...) \ + do {if (mbt_drvdbg & DBG_FATAL) \ + printk(KERN_ERR msg); } while (0) +/** Print message */ +#define PRINTM_MSG(msg...) \ + do {if (mbt_drvdbg & DBG_MSG) \ + printk(KERN_ALERT msg); } while (0) + +/** Print data dump message */ +#define PRINTM_DAT_D(msg...) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data dump message */ +#define PRINTM_CMD_D(msg...) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + printk(KERN_DEBUG msg); } while (0) + +/** Print message with required level */ +#define PRINTM(level, msg...) PRINTM_##level(msg) + +/** Debug dump buffer length */ +#define DBG_DUMP_BUF_LEN 64 +/** Maximum number of dump per line */ +#define MAX_DUMP_PER_LINE 16 +/** Maximum data dump length */ +#define MAX_DATA_DUMP_LEN 48 + +/** + * @brief Prints buffer data upto provided length + * + * @param prompt Char pointer + * @param buf Buffer + * @param len Length + * + * @return N/A + */ +static inline void +hexdump(char *prompt, u8 *buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + printk(KERN_DEBUG "%s: len=%d\n", prompt, len); + for (i = 1; i <= len; i++) { + ptr += snprintf(ptr, 4, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +/** Debug hexdump of debug data */ +#define DBG_HEXDUMP_DAT_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + hexdump(x, y, z); } while (0) +/** Debug hexdump of debug command */ +#define DBG_HEXDUMP_CMD_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + hexdump(x, y, z); } while (0) + +/** Debug hexdump */ +#define DBG_HEXDUMP(level, x, y, z) DBG_HEXDUMP_##level(x, y, z) + +/** Mark entry point */ +#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +/** Mark exit point */ +#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +#else +/** Do nothing */ +#define PRINTM(level, msg...) do {} while (0) +/** Do nothing */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) +/** Do nothing */ +#define ENTER() do {} while (0) +/** Do nothing */ +#define LEAVE() do {} while (0) +#endif /* DEBUG_LEVEL1 */ + +/** Bluetooth upload size */ +#define BT_UPLD_SIZE 2312 +/** Bluetooth status success */ +#define BT_STATUS_SUCCESS (0) +/** Bluetooth status pending */ +#define BT_STATUS_PENDING (1) +/** Bluetooth status failure */ +#define BT_STATUS_FAILURE (-1) + +#ifndef TRUE +/** True value */ +#define TRUE 1 +#endif +#ifndef FALSE +/** False value */ +#define FALSE 0 +#endif + +/** Set thread state */ +#define OS_SET_THREAD_STATE(x) set_current_state(x) +/** Time to wait until Host Sleep state change in millisecond */ +#define WAIT_UNTIL_HS_STATE_CHANGED 2000 +/** Time to wait cmd resp in millisecond */ +#define WAIT_UNTIL_CMD_RESP 5000 + +/** Sleep until a condition gets true or a timeout elapses */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000)) +#else +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) +#endif + +#define os_wait_timeout(waitq, cond, timeout) \ + wait_event_timeout(waitq, cond, ((timeout) * HZ / 1000)) + +/** bt thread structure */ +typedef struct { + /** Task */ + struct task_struct *task; + /** Queue */ + wait_queue_head_t waitQ; + /** PID */ + pid_t pid; + /** Private structure */ + void *priv; +} bt_thread; + +/** + * @brief Activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_activate_thread(bt_thread *thr) +{ + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitQ); + + /** Record the thread pid */ + thr->pid = current->pid; +} + +/** + * @brief De-activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_deactivate_thread(bt_thread *thr) +{ + thr->pid = 0; + return; +} + +/** + * @brief Creates bt thread + * + * @param btfunc Function pointer + * @param thr A pointer to bt_thread structure + * @param name Char pointer + * + * @return N/A + */ +static inline void +bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name) +{ + thr->task = kthread_run(btfunc, thr, "%s", name); +} + +/** + * @brief Delete bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline int +bt_terminate_thread(bt_thread *thr) +{ + /* Check if the thread is active or not */ + if (!thr->pid) + return -1; + + kthread_stop(thr->task); + return 0; +} + +/** + * @brief Set scheduled timeout + * + * @param millisec Time unit in ms + * + * @return N/A + */ +static inline void +os_sched_timeout(u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + + schedule_timeout((millisec * HZ) / 1000); +} + +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__((packed)) +#endif + +/** Data structure for the Marvell Bluetooth device */ +typedef struct _bt_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** m_dev structure */ + struct m_dev m_dev[MAX_RADIO_FUNC]; + + /** Tx download ready flag */ + u8 tx_dnld_rdy; + /** Function */ + u8 fn; + /** Rx unit */ + u8 rx_unit; + /** Power Save mode : Timeout configuration */ + u16 idle_timeout; + /** Power Save mode */ + u8 psmode; + /** Power Save command */ + u8 pscmd; + /** Host Sleep mode */ + u8 hsmode; + /** Host Sleep command */ + u8 hscmd; + /** Low byte is gap, high byte is GPIO */ + u16 gpio_gap; + /** Host Sleep configuration command */ + u8 hscfgcmd; + /** Host Send Cmd Flag */ + u8 sendcmdflag; + /** opcode for Send Cmd */ + u16 send_cmd_opcode; + /** Device Type */ + u8 devType; + /** Device Features */ + u8 devFeature; + /** cmd52 function */ + u8 cmd52_func; + /** cmd52 register */ + u8 cmd52_reg; + /** cmd52 value */ + u8 cmd52_val; + /** SDIO pull control command */ + u8 sdio_pull_ctrl; + /** Low 2 bytes is pullUp, high 2 bytes for pull-down */ + u32 sdio_pull_cfg; + /** Test mode command */ + u8 test_mode; +} bt_dev_t, *pbt_dev_t; + +/** Marvell bt adapter structure */ +typedef struct _bt_adapter { + /** Chip revision ID */ + u8 chip_rev; + /** magic val */ + u8 magic_val; + /** Surprise removed flag */ + u8 SurpriseRemoved; + /** IRQ number */ + int irq; + /** Interrupt counter */ + u32 IntCounter; + /** Tx packet queue */ + struct sk_buff_head tx_queue; + + /** Pointer of fw dump file name */ + char *fwdump_fname; + /** Pending Tx packet queue */ + struct sk_buff_head pending_queue; + /** tx lock flag */ + u8 tx_lock; + /** Power Save mode */ + u8 psmode; + /** Power Save state */ + u8 ps_state; + /** Host Sleep state */ + u8 hs_state; + /** hs skip count */ + u32 hs_skip; + /** suspend_fail flag */ + u8 suspend_fail; + /** suspended flag */ + u8 is_suspended; + /** Number of wakeup tries */ + u8 WakeupTries; + /** Host Sleep wait queue */ + wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__; + /** Host Cmd complet state */ + u8 cmd_complete; + /** indicate using wait event timeout */ + u8 wait_event_timeout; + /** last irq recv */ + u8 irq_recv; + /** last irq processed */ + u8 irq_done; + /** sdio int status */ + u8 sd_ireg; + /** buf allocated for transmit */ + u8 *tx_buffer; + /** buf for transmit */ + u8 *tx_buf; + /** buf allocated for read interrupt status */ + u8 *hw_regs_buf; + /** buf for read interrupt status */ + u8 *hw_regs; + /** tx pending */ + u32 skb_pending; +/** Version string buffer length */ +#define MAX_VER_STR_LEN 128 + /** Driver version */ + u8 drv_ver[MAX_VER_STR_LEN]; + /** Number of command timeout */ + u32 num_cmd_timeout; +} bt_adapter, *pbt_adapter; + +/** Length of prov name */ +#define PROC_NAME_LEN 32 + +/** Item data structure */ +struct item_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** Size */ + u32 size; + /** Address */ + t_ptr addr; + /** Offset */ + u32 offset; + /** Flag */ + u32 flag; +}; + +/** Proc private data structure */ +struct proc_private_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** File flag */ + u32 fileflag; + /** Buffer size */ + u32 bufsize; + /** Number of items */ + u32 num_items; + /** Item data */ + struct item_data *pdata; + /** Private structure */ + struct _bt_private *pbt; + /** File operations */ + const struct file_operations *fops; +}; + +/** Device proc structure */ +struct device_proc { + /** Proc directory entry */ + struct proc_dir_entry *proc_entry; + /** num of proc files */ + u8 num_proc_files; + /** pointer to proc_private_data */ + struct proc_private_data *pfiles; +}; + +/** Private structure for the MV device */ +typedef struct _bt_private { + /** Bluetooth device */ + bt_dev_t bt_dev; + /** Adapter */ + bt_adapter *adapter; + /** Firmware helper */ + const struct firmware *fw_helper; + /** Firmware */ + const struct firmware *firmware; + /** Init user configure file */ + const struct firmware *init_user_cfg; + /** Init user configure wait queue token */ + u16 init_user_conf_wait_flag; + /** Init user configure file wait queue */ + wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__; + /** Firmware request start time */ + struct timeval req_fw_time; + /** Hotplug device */ + struct device *hotplug_device; + /** thread to service interrupts */ + bt_thread MainThread; + /** proc data */ + struct device_proc dev_proc[MAX_RADIO_FUNC]; + /** Driver lock */ + spinlock_t driver_lock; + /** Driver lock flags */ + ulong driver_flags; + /** Driver reference flags */ + struct kobject kobj; + /** CRC check flag */ + u16 fw_crc_check; + /** Card type */ + u16 card_type; + /** sdio device */ + const struct sdio_device *psdio_device; + int debug_device_pending; + int debug_ocf_ogf[2]; + u8 fw_reload; +#ifdef BLE_WAKEUP + u8 ble_wakeup_buf_size; + u8 *ble_wakeup_buf; + /** white list address: address count + count* address*/ + u8 white_list[61]; +#endif + /** fw dump state */ + u8 fw_dump; +} bt_private, *pbt_private; + +/** Disable interrupt */ +#define OS_INT_DISABLE spin_lock_irqsave(&priv->driver_lock, \ + priv->driver_flags) +/** Enable interrupt */ +#define OS_INT_RESTORE spin_unlock_irqrestore(&priv->driver_lock, \ + priv->driver_flags) + +#ifndef HCI_BT_AMP +/** BT_AMP flag for device type */ +#define HCI_BT_AMP 0x80 +#endif + +/** Device type of BT */ +#define DEV_TYPE_BT 0x00 +/** Device type of AMP */ +#define DEV_TYPE_AMP 0x01 + +/** Marvell vendor packet */ +#define MRVL_VENDOR_PKT 0xFE + +/** Bluetooth command : Get FW Version */ +#define BT_CMD_GET_FW_VERSION 0x0F +/** Bluetooth command : Sleep mode */ +#define BT_CMD_AUTO_SLEEP_MODE 0x23 +/** Bluetooth command : Host Sleep configuration */ +#define BT_CMD_HOST_SLEEP_CONFIG 0x59 +/** Bluetooth command : Host Sleep enable */ +#define BT_CMD_HOST_SLEEP_ENABLE 0x5A +/** Bluetooth command : Module Configuration request */ +#define BT_CMD_MODULE_CFG_REQ 0x5B +#ifdef BLE_WAKEUP +/** Bluetooth command : Get whitelist */ +#define BT_CMD_GET_WHITELIST 0x9C + +#define HCI_BLE_GRP_BLE_CMDS 0x08 +#define HCI_BT_SET_EVENTMASK_OCF 0x0001 +#define HCI_BLE_ADD_DEV_TO_WHITELIST_OCF 0x0011 +#define HCI_BLE_SET_SCAN_PARAMETERS_OCF 0x000B +#define HCI_BLE_SET_SCAN_ENABLE_OCF 0x000C + +#endif +/** Bluetooth command : PMIC Configure */ +#define BT_CMD_PMIC_CONFIGURE 0x7D + +/** Bluetooth command : SDIO pull up down configuration request */ +#define BT_CMD_SDIO_PULL_CFG_REQ 0x69 +/** Bluetooth command : Set Evt Filter Command */ +#define BT_CMD_SET_EVT_FILTER 0x05 +/** Bluetooth command : Enable Write Scan Command */ +#define BT_CMD_ENABLE_WRITE_SCAN 0x1A +/** Bluetooth command : Enable Device under test mode */ +#define BT_CMD_ENABLE_DEVICE_TESTMODE 0x03 +/** Sub Command: Module Bring Up Request */ +#define MODULE_BRINGUP_REQ 0xF1 +/** Sub Command: Module Shut Down Request */ +#define MODULE_SHUTDOWN_REQ 0xF2 +/** Module already up */ +#define MODULE_CFG_RESP_ALREADY_UP 0x0c +/** Sub Command: Host Interface Control Request */ +#define MODULE_INTERFACE_CTRL_REQ 0xF5 + +/** Bluetooth event : Power State */ +#define BT_EVENT_POWER_STATE 0x20 + +/** Bluetooth Power State : Enable */ +#define BT_PS_ENABLE 0x02 +/** Bluetooth Power State : Disable */ +#define BT_PS_DISABLE 0x03 +/** Bluetooth Power State : Sleep */ +#define BT_PS_SLEEP 0x01 +/** Bluetooth Power State : Awake */ +#define BT_PS_AWAKE 0x02 + +/** Vendor OGF */ +#define VENDOR_OGF 0x3F +/** OGF for reset */ +#define RESET_OGF 0x03 +/** Bluetooth command : Reset */ +#define BT_CMD_RESET 0x03 + +/** Host Sleep activated */ +#define HS_ACTIVATED 0x01 +/** Host Sleep deactivated */ +#define HS_DEACTIVATED 0x00 + +/** Power Save sleep */ +#define PS_SLEEP 0x01 +/** Power Save awake */ +#define PS_AWAKE 0x00 + +/** bt header length */ +#define BT_HEADER_LEN 4 + +#ifndef MAX +/** Return maximum of two */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/** This is for firmware specific length */ +#define EXTRA_LEN 36 + +/** Command buffer size for Marvell driver */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Bluetooth Rx packet buffer size for Marvell driver */ +#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \ + (HCI_MAX_FRAME_SIZE + EXTRA_LEN) + +/** Buffer size to allocate */ +#define ALLOC_BUF_SIZE (((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \ + MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \ + + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) + +/** Request FW timeout in second */ +#define REQUEST_FW_TIMEOUT 30 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** The number of times to try when waiting for downloaded firmware to + become active when multiple interface is present */ +#define MAX_MULTI_INTERFACE_POLL_TRIES 150 + +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** default idle time */ +#define DEFAULT_IDLE_TIME 1000 + +#define BT_CMD_HEADER_SIZE 3 + +#define BT_CMD_DATA_LEN 128 +#define BT_EVT_DATA_LEN 8 + +/** BT command structure */ +typedef struct _BT_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** Data */ + u8 data[BT_CMD_DATA_LEN]; +} __ATTRIB_PACK__ BT_CMD; + +/** BT event structure */ +typedef struct _BT_EVENT { + /** Event Counter */ + u8 EC; + /** Length */ + u8 length; + /** Data */ + u8 data[BT_EVT_DATA_LEN]; +} BT_EVENT; + +#if defined(SDIO_SUSPEND_RESUME) +#define DEF_GPIO_GAP 0xffff +#endif + +#ifdef BLE_WAKEUP +#define BD_ADDR_SIZE 6 +/** Vendor specific event */ +#define VENDOR_SPECIFIC_EVENT 0xff +/** system suspend event */ +#define HCI_SYSTEM_SUSPEND_EVT 0x80 +/** system suspend */ +#define HCI_SYSTEM_SUSPEND 0x00 +/** system resume */ +#define HCI_SYSTEM_RESUME 0x01 +/** This function enables ble wake up pattern */ +int bt_config_ble_wakeup(bt_private *priv, bool is_shutdown); +int bt_send_system_event(bt_private *priv, u8 flag); +void bt_send_hw_remove_event(bt_private *priv); +#endif + +/** This function verify the received event pkt */ +int check_evtpkt(bt_private *priv, struct sk_buff *skb); + +/* Prototype of global function */ +/** This function gets the priv reference */ +struct kobject *bt_priv_get(bt_private *priv); +/** This function release the priv reference */ +void bt_priv_put(bt_private *priv); +/** This function adds the card */ +bt_private *bt_add_card(void *card); +/** This function removes the card */ +int bt_remove_card(void *card); +/** This function handles the interrupt */ +void bt_interrupt(struct m_dev *m_dev); + +/** This function creates proc interface directory structure */ +int bt_root_proc_init(void); +/** This function removes proc interface directory structure */ +int bt_root_proc_remove(void); +/** This function initializes proc entry */ +int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq); +/** This function removes proc interface */ +void bt_proc_remove(bt_private *priv); + +/** This function process the received event */ +int bt_process_event(bt_private *priv, struct sk_buff *skb); +/** This function enables host sleep */ +int bt_enable_hs(bt_private *priv, bool is_shutdown); +/** This function used to send command to firmware */ +int bt_prepare_command(bt_private *priv); +/** This function frees the structure of adapter */ +void bt_free_adapter(bt_private *priv); +/** This function handle the receive packet */ +void bt_recv_frame(bt_private *priv, struct sk_buff *skb); +void bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len); + +/** clean up m_devs */ +void clean_up_m_devs(bt_private *priv); +/** bt driver call this function to register to bus driver */ +int *sbi_register(void); +/** bt driver call this function to unregister to bus driver */ +void sbi_unregister(void); +/** bt driver calls this function to register the device */ +int sbi_register_dev(bt_private *priv); +/** bt driver calls this function to unregister the device */ +int sbi_unregister_dev(bt_private *priv); +/** This function initializes firmware */ +int sbi_download_fw(bt_private *priv); +/** Configures hardware to quit deep sleep state */ +int sbi_wakeup_firmware(bt_private *priv); +/** Module configuration and register device */ +int sbi_register_conf_dpc(bt_private *priv); + +/** This function is used to send the data/cmd to hardware */ +int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb); +/** This function reads the current interrupt status register */ +int sbi_get_int_status(bt_private *priv); +/** This function enables the host interrupts */ +int sbi_enable_host_int(bt_private *priv); +/** This function disables the host interrupts */ +int sbi_disable_host_int(bt_private *priv); + +/** bt fw reload flag */ +extern int bt_fw_reload; +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 +/** This function reload firmware */ +void bt_request_fw_reload(bt_private *priv, int mode); +#define MAX_TX_BUF_SIZE 2312 +/** This function downloads firmware image to the card */ +int sd_download_firmware_w_helper(bt_private *priv); +void bt_dump_sdio_regs(bt_private *priv); +#define FW_DUMP_TYPE_ENDED 0x002 +#define FW_DUMP_TYPE_MEM_ITCM 0x004 +#define FW_DUMP_TYPE_MEM_DTCM 0x005 +#define FW_DUMP_TYPE_MEM_SQRAM 0x006 +#define FW_DUMP_TYPE_MEM_IRAM 0x007 +#define FW_DUMP_TYPE_REG_MAC 0x009 +#define FW_DUMP_TYPE_REG_CIU 0x00E +#define FW_DUMP_TYPE_REG_APU 0x00F +#define FW_DUMP_TYPE_REG_ICU 0x014 +/* dumps the firmware to /var/ or /data/ */ +void bt_dump_firmware_info_v2(bt_private *priv); + +/** Max line length allowed in init config file */ +#define MAX_LINE_LEN 256 +/** Max MAC address string length allowed */ +#define MAX_MAC_ADDR_LEN 18 +/** Max register type/offset/value etc. parameter length allowed */ +#define MAX_PARAM_LEN 12 + +/** Bluetooth command : Mac address configuration */ +#define BT_CMD_CONFIG_MAC_ADDR 0x22 +/** Bluetooth command : Write CSU register */ +#define BT_CMD_CSU_WRITE_REG 0x66 +/** Bluetooth command : Load calibrate data */ +#define BT_CMD_LOAD_CONFIG_DATA 0x61 +/** Bluetooth command : Load calibrate ext data */ +#define BT_CMD_LOAD_CONFIG_DATA_EXT 0x60 + +/** Bluetooth command : BLE deepsleep */ +#define BT_CMD_BLE_DEEP_SLEEP 0x8b + +/** BT_BLE command structure */ +typedef struct _BT_BLE_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** deepsleep flag */ + u8 deepsleep; +} __ATTRIB_PACK__ BT_BLE_CMD; + +/** BT_CSU command structure */ +typedef struct _BT_CSU_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** reg type */ + u8 type; + /** address */ + u8 offset[4]; + /** Data */ + u8 value[2]; +} __ATTRIB_PACK__ BT_CSU_CMD; + +/** This function sets mac address */ +int bt_set_mac_address(bt_private *priv, u8 *mac); +/** This function writes value to CSU registers */ +int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value); +/** BT set user defined init data and param */ +int bt_init_config(bt_private *priv, char *cfg_file); +/** BT set uer defined init commands */ +int bt_init_cmds(bt_private *priv, char *init_cmds_file); +/** BT process command */ +int bt_process_commands(bt_private *priv, u8 *cmd_data, u32 cmd_len); +/** BT PMIC Configure command */ +int bt_pmic_configure(bt_private *priv); +/** This function load the calibrate data */ +int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac); +/** This function load the calibrate ext data */ +int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len); +/** BT set user defined calibration data */ +int bt_cal_config(bt_private *priv, char *cfg_file, char *mac); +/** BT set user defined calibration ext data */ +int bt_cal_config_ext(bt_private *priv, char *cfg_file); +int bt_init_mac_address(bt_private *priv, char *mac); + +int bt_set_independent_reset(bt_private *priv); +/** Bluetooth command : Independent reset */ +#define BT_CMD_INDEPENDENT_RESET 0x0D + +/** BT HCI command structure */ +typedef struct _BT_HCI_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** cmd type */ + u8 cmd_type; + /** cmd len */ + u8 cmd_len; + /** Data */ + u8 data[6]; +} __ATTRIB_PACK__ BT_HCI_CMD; + +#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8987/bt_char/bt_init.c b/bt_sd8987/bt_char/bt_init.c new file mode 100644 index 0000000..270a4aa --- /dev/null +++ b/bt_sd8987/bt_char/bt_init.c
@@ -0,0 +1,871 @@ +/** @file bt_init.c + * + * @brief This file contains the init functions for BlueTooth + * driver. + * + * Copyright (C) 2011-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/string.h> +#include <linux/firmware.h> + +#include "bt_drv.h" + +extern int bt_req_fw_nowait; + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) + +#define isdigit(c) (('0' <= (c) && (c) <= '9')) +#define isspace(c) (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9))) +/** + * @brief Returns hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +static int +bt_hexval(char chr) +{ + ENTER(); + + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + LEAVE(); + return 0; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +static char * +bt_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Returns hex value of a given ascii string + * + * @param a String to be converted + * + * @return hex value + */ +static int +bt_atox(const char *a) +{ + int i = 0; + ENTER(); + while (isxdigit(*a)) + i = i * 16 + bt_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Converts mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +static void +bt_mac2u8(u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + memcpy(mac_buff, buf, strlen(buf)); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = bt_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = bt_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +/** + * @brief Returns integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_atoi(int *data, char *a) +{ + int i, val = 0, len; + + ENTER(); + + len = strlen(a); + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = bt_atox(a); + return BT_STATUS_SUCCESS; + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + PRINTM(ERROR, "Invalid char %c in string %s\n", a[i], + a); + return BT_STATUS_FAILURE; + } + } + *data = val; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief parse cal-data + * + * @param src a pointer to cal-data string + * @param len len of cal-data + * @param dst a pointer to return cal-data + * @param dst_size size of dest buffer + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size) +{ + const u8 *ptr; + u8 *dptr; + u32 count = 0; + int ret = BT_STATUS_FAILURE; + + ENTER(); + ptr = src; + dptr = dst; + + while ((ptr - src) < len) { + if (*ptr && isspace(*ptr)) { + ptr++; + continue; + } + + if (isxdigit(*ptr)) { + if ((dptr - dst) >= *dst_size) { + PRINTM(ERROR, "cal_file size too big!!!\n"); + goto done; + } + *dptr++ = bt_atox((const char *)ptr); + ptr += 2; + count++; + } else { + ptr++; + } + } + if (dptr == dst) { + ret = BT_STATUS_FAILURE; + goto done; + } + + *dst_size = count; + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief parse ASCII format raw data to hex format + * + * @param priv bt_private + * @param data Source data + * @param size Source data length + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +bt_process_init_raw_cmds(bt_private *priv, u8 *data, u32 size) +{ + int ret = 0; + u8 *pos = data; + u8 *intf_s, *intf_e; + u8 *buf = NULL; + u8 *ptr = NULL; + u8 cmd_len = 0; + bool start_raw = false; + gfp_t flag; + + flag = (in_atomic() || irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(MRVDRV_SIZE_OF_CMD_BUFFER, flag); + + if (!buf) { + PRINTM(ERROR, "Could not allocate buffer space!\n"); + return -EFAULT; + } + ptr = buf; + while ((pos - data) < size) { + while ((*pos == ' ' || *pos == '\t') && ((pos - data) < size)) + pos++; + if (*pos == '#') { /* Line comment */ + while ((*pos != '\n') && ((pos - data) < size)) + pos++; + pos++; + } + if ((*pos == '\r' && *(pos + 1) == '\n') || *pos == '\n' || + *pos == '\0') { + pos++; + continue; /* Needn't process this line */ + } + + if (*pos == '}') { + /* For hostcmd data conf */ + cmd_len = *(buf + sizeof(u16)); + ret = bt_process_commands(priv, buf, + cmd_len + BT_CMD_HEADER_SIZE); + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + ptr = buf; + start_raw = false; + pos++; + continue; + } + + if (start_raw == false) { + intf_s = strchr(pos, '='); + if (intf_s) + intf_e = strchr(intf_s, '{'); + else + intf_e = NULL; + + if (intf_s && intf_e) { + start_raw = true; + pos = intf_e + 1; + continue; + } + } + + if (start_raw) { + /* Raw data block exists */ + while ((*pos != '\n') && ((pos - data) < size)) { + if (isxdigit(*pos)) { + if((ptr-buf) < MRVDRV_SIZE_OF_CMD_BUFFER) + *ptr++ = bt_atox(pos); + pos += 2; + } else + pos++; + } + } + } + kfree(buf); + return ret; +} + +/** + * @brief BT set user init commands + * + * @param priv BT private handle + * @param init_cmds_file user init commands file + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ + +int +bt_init_cmds(bt_private *priv, char *init_cmds_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if ((request_firmware(&cfg, init_cmds_file, priv->hotplug_device)) < 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + init_cmds_file); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cfg) { + ret = bt_process_init_raw_cmds(priv, (u8 *)cfg->data, + cfg->size); + } + else { + ret = BT_STATUS_FAILURE; + } +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return -1 or length of the line + */ +int +parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos) +{ + static s32 pos; + u8 *src, *dest; + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while ((dest - line_pos < MAX_LINE_LEN - 1) && pos < size && + *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + *dest = '\0'; + /* parse new line */ + pos++; + return strlen((const char *)line_pos); +} + +/** + * @brief BT parse ASCII format data to MAC address + * + * @param priv BT private handle + * @param data Source data + * @param size data length + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_init_cfg(bt_private *priv, u8 *data, u32 size) +{ + u8 *pos; + u8 *intf_s, *intf_e; + u8 s[MAX_LINE_LEN]; /* 1 line data */ + u32 line_len; + char dev_name[MAX_PARAM_LEN]; + u8 buf[MAX_PARAM_LEN]; + u8 bt_addr[MAX_MAC_ADDR_LEN]; + u8 bt_mac[ETH_ALEN]; + int setting = 0; + u8 type = 0; + u16 value = 0; + u32 offset = 0; + int ret = BT_STATUS_FAILURE; + + memset(dev_name, 0, sizeof(dev_name)); + memset(bt_addr, 0, sizeof(bt_addr)); + memset(bt_mac, 0, sizeof(bt_mac)); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Need n't process this line */ + + /* Process MAC addr */ + if (strncmp((char *)pos, "mac_addr", 8) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(dev_name, (const char *)intf_s + 1, + intf_e - intf_s - 1); + dev_name[intf_e - intf_s - 1] = '\0'; + strncpy((char *)bt_addr, + (const char *)intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + /* Convert MAC format */ + bt_mac2u8(bt_mac, (char *)bt_addr); + PRINTM(CMD, + "HCI: %s new BT Address " MACSTR "\n", + dev_name, MAC2STR(bt_mac)); + if (BT_STATUS_SUCCESS != + bt_set_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp((char *)pos, "bt_reg", 6) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s + 1, + 1); + buf[1] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + type = (u8)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg type\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = (u8 *)strchr((const char *)intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, + intf_e - intf_s); + buf[intf_e - intf_s] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + offset = (u32)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg offset\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) { + PRINTM(ERROR, + "BT: Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, sizeof(buf)); + if (0 == bt_atoi(&setting, (char *)buf)) + value = (u16) setting; + else { + PRINTM(ERROR, "BT: Fail to parse reg value\n"); + goto done; + } + + PRINTM(CMD, + "BT: Write reg type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + if (BT_STATUS_SUCCESS != + bt_write_reg(priv, type, offset, value)) { + PRINTM(FATAL, + "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + goto done; + } + } + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to bt_private structure + * + * @return N/A + */ +static void +bt_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + bt_private *priv = (bt_private *)context; + + ENTER(); + + if (!firmware) + PRINTM(ERROR, "BT user init config request firmware failed\n"); + + priv->init_user_cfg = firmware; + priv->init_user_conf_wait_flag = TRUE; + wake_up_interruptible(&priv->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief BT set user defined init data and param + * + * @param priv BT private handle + * @param cfg_file user cofig file + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_config(bt_private *priv, char *cfg_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cfg) + ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + u8 cal_data[32]; + u8 *mac_data = NULL; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + u8 *pcal_data = cal_data; + + memset(bt_mac, 0, sizeof(bt_mac)); + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (mac != NULL) { + /* Convert MAC format */ + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: new BT Address " MACSTR "\n", + MAC2STR(bt_mac)); + mac_data = bt_mac; + } + if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size) +{ + u8 cal_data[128]; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (BT_STATUS_SUCCESS != + bt_load_cal_data_ext(priv, cal_data, cal_data_len)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config(bt_private *priv, char *cal_file, char *mac) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config_ext(bt_private *priv, char *cal_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT init mac address from bt_mac parametre when insmod + * + * @param priv a pointer to bt_private structure + * @param bt_mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_mac_address(bt_private *priv, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + int ret = BT_STATUS_FAILURE; + + ENTER(); + memset(bt_mac, 0, sizeof(bt_mac)); + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac)); + ret = bt_set_mac_address(priv, bt_mac); + if (ret != BT_STATUS_SUCCESS) + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre.\n"); + + LEAVE(); + return ret; +}
diff --git a/bt_sd8987/bt_char/bt_main.c b/bt_sd8987/bt_char/bt_main.c new file mode 100644 index 0000000..ffd0936 --- /dev/null +++ b/bt_sd8987/bt_char/bt_main.c
@@ -0,0 +1,4083 @@ +/** @file bt_main.c + * + * @brief This file contains the major functions in BlueTooth + * driver. It includes init, exit, open, close and main + * thread etc.. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/** + * @mainpage M-BT Linux Driver + * + * @section overview_sec Overview + * + * The M-BT is a Linux reference driver for Marvell Bluetooth chipset. + * + * @section copyright_sec Copyright + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + */ +#include <linux/module.h> + +#ifdef CONFIG_OF +#include <linux/of.h> +#endif + +#include <linux/mmc/sdio_func.h> + +#include "bt_drv.h" +#include "mbt_char.h" +#include "bt_sdio.h" + +/** Version */ +#define VERSION "C4X14114" + +/** Driver version */ +static char mbt_driver_version[] = "SD8XXX-%s-" VERSION "-(" "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** SD8787 Card */ +#define CARD_SD8787 "SD8787" +/** SD8777 Card */ +#define CARD_SD8777 "SD8777" +/** SD8887 Card */ +#define CARD_SD8887 "SD8887" +/** SD8897 Card */ +#define CARD_SD8897 "SD8897" +/** SD8797 Card */ +#define CARD_SD8797 "SD8797" +/** SD8977 Card */ +#define CARD_SD8977 "SD8977" +/** SD8978 Card */ +#define CARD_SD8978 "SD8978" +/** SD8997 Card */ +#define CARD_SD8997 "SD8997" +/** SD8987 Card */ +#define CARD_SD8987 "SD8987" + +/** Declare and initialize fw_version */ +static char fw_version[32] = "0.0.0.p0"; + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ + +#define AID_NET_BT_STACK 3008 /* bluetooth stack */ + +/** Define module name */ + +#define MODULE_NAME "bt_fm_nfc" + +/** Declaration of chardev class */ +static struct class *chardev_class; + +/** Interface specific variables */ +static int mbtchar_minor; +static int debugchar_minor; + +/** + * The global variable of a pointer to bt_private + * structure variable + **/ +bt_private *m_priv[MAX_BT_ADAPTER]; + +/** Default Driver mode */ +static int drv_mode = (DRV_MODE_BT); + +/** BT interface name */ +static char *bt_name; +/** BT debug interface name */ +static char *debug_name; + +/** fw reload flag */ +int bt_fw_reload; +/** fw serial download flag */ +int bt_fw_serial = 1; + +/** Firmware flag */ +static int fw = 1; +/** default powermode */ +static int psmode = 1; +/** default BLE deep sleep */ +static int deep_sleep = 1; +/** Default CRC check control */ +static int fw_crc_check = 1; +/** init cmds file */ +static char *init_cmds; +/** Init config file (MAC address, register etc.) */ +static char *init_cfg; +/** Calibration config file (MAC address, init powe etc.) */ +static char *cal_cfg; +/** Calibration config file EXT */ +static char *cal_cfg_ext; +/** Init MAC address */ +static char *bt_mac; +static int btindrst = -1; + +/** Setting mbt_drvdbg value based on DEBUG level */ +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff & ~DBG_EVENT) +#else +#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) +#endif /* DEBUG_LEVEL2 */ +u32 mbt_drvdbg = DEFAULT_DEBUG_MASK; +#endif + +#ifdef CONFIG_OF +static int dts_enable = 1; +#endif + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int mbt_pm_keep_power = 1; +#endif + +static int debug_intf = 1; + +static int btpmic = 0; + +/** Offset of sequence number in event */ +#define OFFSET_SEQNUM 4 + +/** + * @brief handle received packet + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * + * @return N/A + */ +void +bt_recv_frame(bt_private *priv, struct sk_buff *skb) +{ + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + mdev_bt->stat.byte_rx += skb->len; + mdev_recv_frame(skb); + } + return; +} + +/** + * @brief Alloc bt device + * + * @return pointer to structure mbt_dev or NULL + */ +struct mbt_dev * +alloc_mbt_dev(void) +{ + struct mbt_dev *mbt_dev; + ENTER(); + + mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL); + if (!mbt_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return mbt_dev; +} + +/** + * @brief Alloc debug device + * + * @return pointer to structure debug_level or NULL + */ +struct debug_dev * +alloc_debug_dev(void) +{ + struct debug_dev *debug_dev; + ENTER(); + + debug_dev = kzalloc(sizeof(struct debug_dev), GFP_KERNEL); + if (!debug_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return debug_dev; +} + +/** + * @brief Frees m_dev + * + * @return N/A + */ +void +free_m_dev(struct m_dev *m_dev) +{ + ENTER(); + kfree(m_dev->dev_pointer); + m_dev->dev_pointer = NULL; + LEAVE(); +} + +/** + * @brief clean up m_devs + * + * @return N/A + */ +void +clean_up_m_devs(bt_private *priv) +{ + struct m_dev *m_dev = NULL; + int i; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if (m_dev->spec_type == IANYWHERE_SPEC) { + if ((drv_mode & DRV_MODE_BT) && (mbtchar_minor > 0)) + mbtchar_minor--; + m_dev->close(m_dev); + for (i = 0; i < 3; i++) + kfree_skb(((struct mbt_dev *) + (m_dev->dev_pointer))-> + reassembly[i]); + /** unregister m_dev to char_dev */ + if (chardev_class) + chardev_cleanup_one(m_dev, chardev_class); + free_m_dev(m_dev); + } + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL; + } + if (priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if ((debug_intf) && (debugchar_minor > 0)) + debugchar_minor--; + /** unregister m_dev to char_dev */ + if (chardev_class) + chardev_cleanup_one(m_dev, chardev_class); + free_m_dev(m_dev); + priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = NULL; + } + LEAVE(); + return; +} + +/** + * @brief This function verify the received event pkt + * + * Event format: + * +--------+--------+--------+--------+--------+ + * | Event | Length | ncmd | Opcode | + * +--------+--------+--------+--------+--------+ + * | 1-byte | 1-byte | 1-byte | 2-byte | + * +--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +check_evtpkt(bt_private *priv, struct sk_buff *skb) +{ + struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data; + struct hci_ev_cmd_complete *ec; + u16 opcode, ocf; + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (!priv->bt_dev.sendcmdflag) { + ret = BT_STATUS_FAILURE; + goto exit; + } + if (hdr->evt == HCI_EV_CMD_COMPLETE) { + ec = (struct hci_ev_cmd_complete *) + (skb->data + HCI_EVENT_HDR_SIZE); + opcode = __le16_to_cpu(ec->opcode); + ocf = hci_opcode_ocf(opcode); + PRINTM(CMD, + "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n", + opcode, ocf, priv->bt_dev.send_cmd_opcode); + if (opcode != priv->bt_dev.send_cmd_opcode) { + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (ocf) { + case BT_CMD_MODULE_CFG_REQ: + case BT_CMD_BLE_DEEP_SLEEP: + case BT_CMD_CONFIG_MAC_ADDR: + case BT_CMD_CSU_WRITE_REG: + case BT_CMD_LOAD_CONFIG_DATA: + case BT_CMD_LOAD_CONFIG_DATA_EXT: + case BT_CMD_AUTO_SLEEP_MODE: + case BT_CMD_HOST_SLEEP_CONFIG: + case BT_CMD_SDIO_PULL_CFG_REQ: + case BT_CMD_SET_EVT_FILTER: + // case BT_CMD_ENABLE_DEVICE_TESTMODE: + case BT_CMD_PMIC_CONFIGURE: + case BT_CMD_INDEPENDENT_RESET: + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter->cmd_wait_q); + break; + case BT_CMD_GET_FW_VERSION: + { + u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + snprintf(fw_version, sizeof(fw_version), + "%u.%u.%u.p%u", pos[2], pos[1], pos[0], + pos[3]); + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } +#ifdef BLE_WAKEUP + case BT_CMD_GET_WHITELIST: + { + u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + + if ((hdr->plen - + sizeof(struct hci_ev_cmd_complete) - 1) <= + sizeof(priv->white_list)) + memcpy(priv->white_list, pos, + hdr->plen - + sizeof(struct + hci_ev_cmd_complete) - 1); + + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } +#endif + case BT_CMD_RESET: + case BT_CMD_ENABLE_WRITE_SCAN: +#ifdef BLE_WAKEUP + case HCI_BT_SET_EVENTMASK_OCF: + case HCI_BLE_ADD_DEV_TO_WHITELIST_OCF: + case HCI_BLE_SET_SCAN_PARAMETERS_OCF: + case HCI_BLE_SET_SCAN_ENABLE_OCF: +#endif + { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + if (priv->adapter->wait_event_timeout == TRUE) { + wake_up(&priv->adapter->cmd_wait_q); + priv->adapter->wait_event_timeout = + FALSE; + } else + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + break; + case BT_CMD_HOST_SLEEP_ENABLE: + priv->bt_dev.sendcmdflag = FALSE; + break; + default: + /** Ignore command not defined but send by driver */ + if (opcode == priv->bt_dev.send_cmd_opcode) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } else { + ret = BT_STATUS_FAILURE; + } + break; + } + } else + ret = BT_STATUS_FAILURE; +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** +* @brief This function stores the FW dumps received from events +* +* @param priv A pointer to bt_private structure +* @param skb A pointer to rx skb +* +* @return N/A +*/ +void +bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len) +{ + struct file *pfile_fwdump = NULL; + loff_t pos = 0; + u16 seqnum = 0; + struct timeval t; + u32 sec; + + ENTER(); + + seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM)); + + if (priv->adapter->fwdump_fname && seqnum != 1) { + pfile_fwdump = + filp_open((const char *)priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file.\n"); + LEAVE(); + return; + } + } else { + if (!priv->adapter->fwdump_fname) { + gfp_t flag; + flag = (in_atomic() || + irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + priv->adapter->fwdump_fname = kzalloc(64, flag); + } else + memset(priv->adapter->fwdump_fname, 0, 64); + + do_gettimeofday(&t); + sec = (u32)t.tv_sec; + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/var/log/bt_fwdump_", sec); + pfile_fwdump = + filp_open(priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/data/bt_fwdump_", sec); + pfile_fwdump = + filp_open((const char *)priv->adapter-> + fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + } + } + + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file\n"); + LEAVE(); + return; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_fwdump, buf, len, &pos); +#else + kernel_write(pfile_fwdump, buf, len, &pos); +#endif + filp_close(pfile_fwdump, NULL); + LEAVE(); + return; +} + +/** + * @brief This function process the received event + * + * Event format: + * +--------+--------+--------+--------+-----+ + * | EC | Length | Data | + * +--------+--------+--------+--------+-----+ + * | 1-byte | 1-byte | n-byte | + * +--------+--------+--------+--------+-----+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_event(bt_private *priv, struct sk_buff *skb) +{ + int ret = BT_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + BT_EVENT *pevent; + + ENTER(); + if (!m_dev) { + PRINTM(CMD, "BT: bt_process_event without m_dev\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pevent = (BT_EVENT *)skb->data; + if (pevent->EC != 0xff) { + PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC); + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (pevent->data[0]) { + case BT_CMD_AUTO_SLEEP_MODE: + if (pevent->data[2] == BT_STATUS_SUCCESS) { + if (pevent->data[1] == BT_PS_ENABLE) + priv->adapter->psmode = 1; + else + priv->adapter->psmode = 0; + PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name, + (priv->adapter->psmode) ? "Enable" : "Disable"); + + } else { + PRINTM(CMD, "BT: PS Mode Command Fail %s\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_CONFIG: + if (pevent->data[3] == BT_STATUS_SUCCESS) { + PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n", + m_dev->name, pevent->data[1], pevent->data[2]); + } else { + PRINTM(CMD, "BT: %s: HSCFG Command Fail\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_ENABLE: + if (pevent->data[1] == BT_STATUS_SUCCESS) { + priv->adapter->hs_state = HS_ACTIVATED; + if (priv->adapter->suspend_fail == FALSE) { +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED + bt_is_suspended(priv); +#endif +#endif +#endif + if (priv->adapter->wait_event_timeout) { + wake_up(&priv->adapter->cmd_wait_q); + priv->adapter->wait_event_timeout = + FALSE; + } else + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + + } + if (priv->adapter->psmode) + priv->adapter->ps_state = PS_SLEEP; + PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n", + m_dev->name); + + } else { + PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name); + } + break; + case BT_CMD_MODULE_CFG_REQ: + if ((priv->bt_dev.sendcmdflag == TRUE) && + ((pevent->data[1] == MODULE_BRINGUP_REQ) + || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) { + if (pevent->data[1] == MODULE_BRINGUP_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2] && (pevent->data[2] != + MODULE_CFG_RESP_ALREADY_UP)) + ? "Bring up Fail" : "Bring up success"); + priv->bt_dev.devType = pevent->data[3]; + PRINTM(CMD, "devType:%s\n", + (pevent->data[3] == + DEV_TYPE_AMP) ? "AMP controller" : + "BR/EDR controller"); + priv->bt_dev.devFeature = pevent->data[4]; + PRINTM(CMD, "devFeature: %s, %s, %s" + "\n", + ((pevent-> + data[4] & DEV_FEATURE_BT) ? + "BT Feature" : "No BT Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BTAMP) ? + "BTAMP Feature" : "No BTAMP Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BLE) ? + "BLE Feature" : "No BLE Feature") + ); + } + if (pevent->data[1] == MODULE_SHUTDOWN_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2]) ? "Shut down Fail" + : "Shut down success"); + + } + if (pevent->data[2]) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + } else { + PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n"); + ret = BT_STATUS_FAILURE; + } + break; + case BT_EVENT_POWER_STATE: + if (pevent->data[1] == BT_PS_SLEEP) + priv->adapter->ps_state = PS_SLEEP; + if (priv->adapter->ps_state == PS_SLEEP + && (priv->card_type == CARD_TYPE_SD8887) + ) + os_sched_timeout(5); + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); + + break; + case BT_CMD_SDIO_PULL_CFG_REQ: + if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS) + PRINTM(CMD, "BT: %s: SDIO pull configuration success\n", + m_dev->name); + + else { + PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n", + m_dev->name); + + } + break; + default: + PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0], + m_dev->name); + ret = BT_STATUS_FAILURE; + break; + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** + * @brief This function save the dump info to file + * + * @param dir_name directory name + * @param file_name file_name + * @param buf buffer + * @param buf_len buffer length + * + * @return 0 --success otherwise fail + */ +int +bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len) +{ + int ret = BT_STATUS_SUCCESS; + struct file *pfile = NULL; + u8 name[64]; + loff_t pos; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(ERROR, "Can't save dump info to file\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + snprintf((char *)name, sizeof(name), "%s/%s", dir_name, file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + snprintf((char *)name, sizeof(name), "%s/%s", "/var", + file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(ERROR, "Create Dump file for %s error\n", name); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name); + + pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, (const char __user *)buf, buf_len, &pos); + set_fs(fs); +#else + kernel_write(pfile, (const char __user *)buf, buf_len, &pos); +#endif + filp_close(pfile, NULL); + + PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name); + +done: + LEAVE(); + return ret; +} + +#define DEBUG_HOST_READY 0xEE +#define DEBUG_FW_DONE 0xFF +#define DUMP_MAX_POLL_TRIES 200 + +#define DEBUG_DUMP_CTRL_REG_8897 0xE2 +#define DEBUG_DUMP_START_REG_8897 0xE3 +#define DEBUG_DUMP_END_REG_8897 0xEA +#define DEBUG_DUMP_CTRL_REG_8887 0xA2 +#define DEBUG_DUMP_START_REG_8887 0xA3 +#define DEBUG_DUMP_END_REG_8887 0xAA +#define DEBUG_DUMP_CTRL_REG_8977 0xF0 +#define DEBUG_DUMP_START_REG_8977 0xF1 +#define DEBUG_DUMP_END_REG_8977 0xF8 +#define DEBUG_DUMP_CTRL_REG_8978 0xF0 +#define DEBUG_DUMP_START_REG_8978 0xF1 +#define DEBUG_DUMP_END_REG_8978 0xF8 +#define DEBUG_DUMP_CTRL_REG_8997 0xF0 +#define DEBUG_DUMP_START_REG_8997 0xF1 +#define DEBUG_DUMP_END_REG_8997 0xF8 +#define DEBUG_DUMP_CTRL_REG_8987 0xF0 +#define DEBUG_DUMP_START_REG_8987 0xF1 +#define DEBUG_DUMP_END_REG_8987 0xF8 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_APU_REGS = 3, + DUMP_TYPE_CIU_REGS = 4, + DUMP_TYPE_ICU_REGS = 5, + DUMP_TYPE_MAC_REGS = 6, + DUMP_TYPE_EXTEND_7 = 7, + DUMP_TYPE_EXTEND_8 = 8, + DUMP_TYPE_EXTEND_9 = 9, + DUMP_TYPE_EXTEND_10 = 10, + DUMP_TYPE_EXTEND_11 = 11, + DUMP_TYPE_EXTEND_12 = 12, + DUMP_TYPE_EXTEND_13 = 13, + DUMP_TYPE_EXTEND_LAST = 14 +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 + +/** Memory type mapping structure */ +typedef struct { + /** memory name */ + u8 mem_name[MAX_NAME_LEN]; + /** memory pointer */ + u8 *mem_Ptr; + /** file structure */ + struct file *pfile_mem; + /** donbe flag */ + u8 done_flag; + /** dump type */ + u8 type; +} memory_type_mapping; + +memory_type_mapping bt_mem_type_mapping_tbl[] = { + {"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM}, + {"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM}, + {"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM}, + {"APU", NULL, NULL, 0xF3, FW_DUMP_TYPE_REG_APU}, + {"CIU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_CIU}, + {"ICU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_ICU}, + {"MAC", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_MAC}, + {"EXT7", NULL, NULL, 0xF7}, + {"EXT8", NULL, NULL, 0xF8}, + {"EXT9", NULL, NULL, 0xF9}, + {"EXT10", NULL, NULL, 0xFA}, + {"EXT11", NULL, NULL, 0xFB}, + {"EXT12", NULL, NULL, 0xFC}, + {"EXT13", NULL, NULL, 0xFD}, + {"EXTLAST", NULL, NULL, 0xFE}, +}; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +/** + * @brief This function read/write firmware via cmd52 + * + * @param phandle A pointer to moal_handle + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status +bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag) +{ + int ret = 0; + int tries = 0; + u8 ctrl_data = 0; + u8 dbg_dump_ctrl_reg = 0; + + if (priv->card_type == CARD_TYPE_SD8887) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8887; + else if (priv->card_type == CARD_TYPE_SD8897) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8897; + else if (priv->card_type == CARD_TYPE_SD8977) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8977; + else if (priv->card_type == CARD_TYPE_SD8978) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8978; + else if (priv->card_type == CARD_TYPE_SD8997) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8997; + else if (priv->card_type == CARD_TYPE_SD8987) + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG_8987; + + ret = sd_write_reg(priv, dbg_dump_ctrl_reg, DEBUG_HOST_READY); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) { + ret = sd_read_reg(priv, dbg_dump_ctrl_reg, &ctrl_data); + if (ret) { + PRINTM(ERROR, "SDIO READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != DEBUG_HOST_READY) { + PRINTM(INFO, + "The ctrl reg was changed, re-try again!\n"); + ret = sd_write_reg(priv, dbg_dump_ctrl_reg, + DEBUG_HOST_READY); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } + udelay(100); + } + if (ctrl_data == DEBUG_HOST_READY) { + PRINTM(ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void +bt_dump_firmware_info_v2(bt_private *priv) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr = NULL; + u8 dump_num = 0; + u8 idx = 0; + u8 doneflag = 0; + rdwr_status stat; + u8 i = 0; + u8 read_reg = 0; + u32 memory_size = 0; + u8 path_name[64], file_name[32]; + u8 *end_ptr = NULL; + u8 dbg_dump_start_reg = 0; + u8 dbg_dump_end_reg = 0; + + if (!priv) { + PRINTM(ERROR, "Could not dump firmwware info\n"); + return; + } + + if ((priv->card_type != CARD_TYPE_SD8887) && + (priv->card_type != CARD_TYPE_SD8897) + && (priv->card_type != CARD_TYPE_SD8977) && + (priv->card_type != CARD_TYPE_SD8997) + && (priv->card_type != CARD_TYPE_SD8987) && + (priv->card_type != CARD_TYPE_SD8978)) { + PRINTM(MSG, "card_type %d don't support FW dump\n", + priv->card_type); + return; + } + + memset(path_name, 0, sizeof(path_name)); + strcpy((char *)path_name, "/data"); + PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name); + + if (priv->card_type == CARD_TYPE_SD8887) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8887; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8887; + } else if (priv->card_type == CARD_TYPE_SD8897) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8897; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8897; + } else if (priv->card_type == CARD_TYPE_SD8977) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8977; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8977; + } else if (priv->card_type == CARD_TYPE_SD8978) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8978; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8978; + } else if (priv->card_type == CARD_TYPE_SD8997) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8997; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8997; + } else if (priv->card_type == CARD_TYPE_SD8987) { + dbg_dump_start_reg = DEBUG_DUMP_START_REG_8987; + dbg_dump_end_reg = DEBUG_DUMP_END_REG_8987; + } + + sbi_wakeup_firmware(priv); + priv->fw_dump = TRUE; + /* start dump fw memory */ + PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n"); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + reg = dbg_dump_start_reg; + ret = sd_read_reg(priv, reg, &dump_num); + if (ret) { + PRINTM(MSG, "SDIO READ MEM NUM ERR\n"); + goto done; + } + + /* read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + if (RDWR_STATUS_FAILURE == + bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + memory_size = 0; + reg = dbg_dump_start_reg; + for (i = 0; i < 4; i++) { + ret = sd_read_reg(priv, reg, &read_reg); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + if (memory_size == 0) { + PRINTM(MSG, "Firmware Dump Finished!\n"); + break; + } else { + PRINTM(MSG, "%s_SIZE=0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + memory_size); + bt_mem_type_mapping_tbl[idx].mem_Ptr = + vmalloc(memory_size + 1); + if ((ret != BT_STATUS_SUCCESS) || + !bt_mem_type_mapping_tbl[idx].mem_Ptr) { + PRINTM(ERROR, + "Error: vmalloc %s buffer failed!!!\n", + bt_mem_type_mapping_tbl[idx].mem_name); + goto done; + } + dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr; + end_ptr = dbg_ptr + memory_size; + } + doneflag = bt_mem_type_mapping_tbl[idx].done_flag; + PRINTM(MSG, "Start %s output, please wait...\n", + bt_mem_type_mapping_tbl[idx].mem_name); + do { + stat = bt_cmd52_rdwr_firmware(priv, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + ret = sd_read_reg(priv, reg, dbg_ptr); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + PRINTM(MSG, + "pre-allocced buf is not enough\n"); + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MSG, "%s done:" + "size = 0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + (unsigned int)(dbg_ptr - + bt_mem_type_mapping_tbl + [idx].mem_Ptr)); + memset(file_name, 0, sizeof(file_name)); + snprintf((char *)file_name, sizeof(file_name), + "%s%s", "file_bt_", + bt_mem_type_mapping_tbl[idx].mem_name); + if (BT_STATUS_SUCCESS != + bt_save_dump_info_to_file((char *)path_name, + (char *)file_name, + bt_mem_type_mapping_tbl + [idx].mem_Ptr, + memory_size)) + PRINTM(MSG, + "Can't save dump file %s in %s\n", + file_name, path_name); + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + break; + } + } while (1); + } + PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n"); + /* end dump fw memory */ +done: + priv->fw_dump = FALSE; + for (idx = 0; idx < dump_num; idx++) { + if (bt_mem_type_mapping_tbl[idx].mem_Ptr) { + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + } + } + PRINTM(MSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function shows debug info for timeout of command sending. + * + * @param adapter A pointer to bt_private + * @param cmd Timeout command id + * + * @return N/A + */ +static void +bt_cmd_timeout_func(bt_private *priv, u16 cmd) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + + adapter->num_cmd_timeout++; + + PRINTM(ERROR, "Version = %s\n", adapter->drv_ver); + PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd); + PRINTM(ERROR, "Number of command timeout = %d\n", + adapter->num_cmd_timeout); + PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter); + PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode); + PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state); + PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state); + PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip); + PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail); + PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended); + PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries); + PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete); + PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv); + PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done); + PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending); + PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg); + bt_dump_sdio_regs(priv); + LEAVE(); +} + +/** + * @brief This function queue frame + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to sk_buff structure + * + * @return N/A + */ +static void +bt_queue_frame(bt_private *priv, struct sk_buff *skb) +{ + skb_queue_tail(&priv->adapter->tx_queue, skb); +} + +/** + * @brief This function send reset cmd to firmware + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_reset_command(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET); + pcmd->length = 0x00; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, 3); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue Reset Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Reset timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_RESET); + } else { + PRINTM(CMD, "BT: Reset Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends module cfg cmd to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param subcmd sub command + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_module_cfg_cmd(bt_private *priv, int subcmd) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ); + pcmd->length = 1; + pcmd->data[0] = subcmd; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue module cfg Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + /* + On some Android platforms certain delay is needed for HCI daemon to + remove this module and close itself gracefully. Otherwise it hangs. + This 10ms delay is a workaround for such platforms as the root cause + has not been found yet. */ + mdelay(10); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n", + subcmd, priv->bt_dev.sendcmdflag); + bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ); + } else { + PRINTM(CMD, "BT: module cfg Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables power save mode + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_ps(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE); + if (priv->bt_dev.psmode) + pcmd->data[0] = BT_PS_ENABLE; + else + pcmd->data[0] = BT_PS_DISABLE; + if (priv->bt_dev.idle_timeout) { + pcmd->length = 3; + pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff); + pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8; + } else { + pcmd->length = 1; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: psmode timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends hscfg command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_hscfg_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG); + pcmd->length = 2; + pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8; + pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: HSCFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends sdio pull ctrl command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_sdio_pull_ctrl_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ); + pcmd->length = 4; + pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff); + pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8; + pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16; + pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, + "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0], + pcmd->data[3], pcmd->data[2]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends command to configure PMIC + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_pmic_configure(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: PMIC Configure timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables host sleep + * + * @param priv A pointer to bt_private structure + * @param is_shutdown indicate shutdown mode + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_hs(bt_private *priv, bool is_shutdown) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + priv->adapter->suspend_fail = FALSE; + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->adapter->wait_event_timeout = is_shutdown; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + PRINTM(CMD, "Queue hs enable Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (is_shutdown) { + if (!os_wait_timeout + (priv->adapter->cmd_wait_q, priv->adapter->hs_state, + WAIT_UNTIL_HS_STATE_CHANGED)) { + PRINTM(MSG, "BT: Enable host sleep timeout:\n"); + priv->adapter->wait_event_timeout = FALSE; + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE); + } + } else { + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->hs_state, + WAIT_UNTIL_HS_STATE_CHANGED)) { + PRINTM(MSG, "BT: Enable host sleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE); + } + } + OS_INT_DISABLE; + if ((priv->adapter->hs_state == HS_ACTIVATED) || + (priv->adapter->is_suspended == TRUE)) { + OS_INT_RESTORE; + PRINTM(MSG, "BT: suspend success! skip=%d\n", + priv->adapter->hs_skip); + } else { + priv->adapter->suspend_fail = TRUE; + OS_INT_RESTORE; + priv->adapter->hs_skip++; + ret = BT_STATUS_FAILURE; + PRINTM(MSG, + "BT: suspend skipped! " + "state=%d skip=%d ps_state= %d WakeupTries=%d\n", + priv->adapter->hs_state, priv->adapter->hs_skip, + priv->adapter->ps_state, priv->adapter->WakeupTries); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Set Evt Filter + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_evt_filter(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER); + pcmd->length = 0x03; + pcmd->data[0] = 0x02; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set Evt Filter timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Write Scan - Page and Inquiry + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_write_scan(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN); + pcmd->length = 0x01; + pcmd->data[0] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Write Scan timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Device under test mode + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_device_under_testmode(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE); + pcmd->length = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables test mode and send cmd + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_test_mode(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + /** Set Evt Filter Command */ + ret = bt_set_evt_filter(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n"); + goto exit; + } + + /** Enable Write Scan Command */ + ret = bt_enable_write_scan(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n"); + goto exit; + } + + /** Enable Device under test mode */ + ret = bt_enable_device_under_testmode(priv); + if (ret != BT_STATUS_SUCCESS) + PRINTM(ERROR, + "BT test_mode: Enable device under testmode fail\n"); + +exit: + LEAVE(); + return ret; +} + +#define DISABLE_RESET 0x0 +#define ENABLE_OUTBAND_RESET 0x1 +#define ENABLE_INBAND_RESET 0x02 +#define DEFAULT_GPIO 0xff +/** + * @brief This function set GPIO pin + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_independent_reset(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + u8 mode, gpio; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET); + mode = btindrst & 0xff; + gpio = (btindrst & 0xff00) >> 8; + if (mode == ENABLE_OUTBAND_RESET) { + pcmd->data[0] = ENABLE_OUTBAND_RESET; + if (!gpio) + pcmd->data[1] = DEFAULT_GPIO; + else + pcmd->data[1] = gpio; + } else if (mode == ENABLE_INBAND_RESET) { + pcmd->data[0] = ENABLE_INBAND_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else if (mode == DISABLE_RESET) { + pcmd->data[0] = DISABLE_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else { + PRINTM(WARN, "Unsupport mode\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio); + pcmd->length = 2; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Independent reset : timeout!\n"); + bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets ble deepsleep mode + * + * @param priv A pointer to bt_private structure + * @param mode TRUE/FALSE + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_ble_deepsleep(bt_private *priv, int mode) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_BLE_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_BLE_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP); + pcmd->length = 1; + pcmd->deepsleep = mode; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_BLE_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode, + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function gets FW version + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_get_fw_version(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION); + pcmd->length = 0x01; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, 4); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Get FW version: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets mac address + * + * @param priv A pointer to bt_private structure + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_mac_address(bt_private *priv, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + int i = 0; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR); + pcmd->length = 8; + pcmd->cmd_type = MRVL_VENDOR_PKT; + pcmd->cmd_len = 6; + for (i = 0; i < 6; i++) + pcmd->data[i] = mac[5 - i]; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_HCI_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac), + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set mac addr: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate EXT data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_commands(bt_private *priv, u8 *cmd_data, u32 cmd_len) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + u16 ogf = 0; + + ENTER(); + PRINTM(CMD, "BT: init cmds: len=%d\n", cmd_len); + if (cmd_len > BT_CMD_DATA_LEN) { + PRINTM(WARN, "cfg_data_len is too long exceed %d.\n", + BT_CMD_DATA_LEN); + ret = BT_STATUS_FAILURE; + goto exit; + } + + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + memcpy(skb->data, cmd_data, cmd_len); + pcmd = (BT_CMD *)skb->data; + + ogf = hci_opcode_ogf(pcmd->ocf_ogf); + if (ogf == VENDOR_OGF) + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + else + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "init_cmds", skb->data, skb->len); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load init cmds: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + int i = 0; + /* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01 + 0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0xF0}; */ + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA); + pcmd->length = 0x20; + pcmd->data[0] = 0x00; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x00; + pcmd->data[3] = 0x1C; + /* swip cal-data byte */ + for (i = 4; i < 32; i++) + pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i)); + if (mac != NULL) { + pcmd->data[2] = 0x01; /* skip checksum */ + for (i = 24; i < 30; i++) + pcmd->data[i] = mac[29 - i]; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate EXT data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + + ENTER(); + + if (cfg_data_len > BT_CMD_DATA_LEN) { + PRINTM(WARN, "cfg_data_len is too long exceed %d.\n", + BT_CMD_DATA_LEN); + ret = BT_STATUS_FAILURE; + goto exit; + } + + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT); + pcmd->length = cfg_data_len; + + memcpy(pcmd->data, config_data, cfg_data_len); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function writes value to CSU registers + * + * @param priv A pointer to bt_private structure + * @param type reg type + * @param offset register address + * @param value register value to write + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CSU_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CSU_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG); + pcmd->length = 7; + pcmd->type = type; + pcmd->offset[0] = (offset & 0x000000ff); + pcmd->offset[1] = (offset & 0x0000ff00) >> 8; + pcmd->offset[2] = (offset & 0x00ff0000) >> 16; + pcmd->offset[3] = (offset & 0xff000000) >> 24; + pcmd->value[0] = (value & 0x00ff); + pcmd->value[1] = (value & 0xff00) >> 8; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_CSU_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n", + type, offset, value); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Set CSU reg timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function used to restore tx_queue + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_restore_tx_queue(bt_private *priv) +{ + struct sk_buff *skb = NULL; + while (!skb_queue_empty(&priv->adapter->pending_queue)) { + skb = skb_dequeue(&priv->adapter->pending_queue); + if (skb) + bt_queue_frame(priv, skb); + } + wake_up_interruptible(&priv->MainThread.waitQ); +} + +/** + * @brief This function used to send command to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_prepare_command(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (priv->bt_dev.hscfgcmd) { + priv->bt_dev.hscfgcmd = 0; + ret = bt_send_hscfg_cmd(priv); + } + if (priv->bt_dev.pscmd) { + priv->bt_dev.pscmd = 0; + ret = bt_enable_ps(priv); + } + if (priv->bt_dev.sdio_pull_ctrl) { + priv->bt_dev.sdio_pull_ctrl = 0; + ret = bt_send_sdio_pull_ctrl_cmd(priv); + } + if (priv->bt_dev.hscmd) { + priv->bt_dev.hscmd = 0; + if (priv->bt_dev.hsmode) + ret = bt_enable_hs(priv, FALSE); + else { + ret = sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + } + } + if (priv->bt_dev.test_mode) { + priv->bt_dev.test_mode = 0; + ret = bt_enable_test_mode(priv); + } + LEAVE(); + return ret; +} + +/** @brief This function processes a single packet + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to skb which includes TX packet + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +send_single_packet(bt_private *priv, struct sk_buff *skb) +{ + int ret; + struct sk_buff *new_skb = NULL; + ENTER(); + if (!skb || !skb->data) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) { + PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, + BT_UPLD_SIZE); + LEAVE(); + return BT_STATUS_FAILURE; + } + if (skb_headroom(skb) < BT_HEADER_LEN) { + new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN); + if (!new_skb) { + PRINTM(ERROR, "TX error: realloc_headroom failed %d\n", + BT_HEADER_LEN); + kfree_skb(skb); + LEAVE(); + return BT_STATUS_FAILURE; + } else { + if (new_skb != skb) + dev_kfree_skb_any(skb); + skb = new_skb; + } + } + /* This is SDIO/PCIE specific header length: byte[3][2][1], * type: + byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) + */ + skb_push(skb, BT_HEADER_LEN); + skb->data[0] = (skb->len & 0x0000ff); + skb->data[1] = (skb->len & 0x00ff00) >> 8; + skb->data[2] = (skb->len & 0xff0000) >> 16; + skb->data[3] = bt_cb(skb)->pkt_type; + if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT) + PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n", + __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len); + ret = sbi_host_to_card(priv, skb->data, skb->len); + if (ret == BT_STATUS_FAILURE) + ((struct m_dev *)skb->dev)->stat.err_tx++; + else + ((struct m_dev *)skb->dev)->stat.byte_tx += skb->len; + if (ret != BT_STATUS_PENDING) + kfree_skb(skb); + LEAVE(); + return ret; +} + +#ifdef CONFIG_OF +/** + * @brief This function read the initial parameter from device tress + * + * + * @return N/A + */ +static void +bt_init_from_dev_tree(void) +{ + struct device_node *dt_node = NULL; + struct property *prop; + u32 data; + const char *string_data; + + ENTER(); + + if (!dts_enable) { + PRINTM(CMD, "DTS is disabled!"); + return; + } + + dt_node = of_find_node_by_name(NULL, "sd8xxx-bt"); + if (!dt_node) { + LEAVE(); + return; + } + for_each_property_of_node(dt_node, prop) { +#ifdef DEBUG_LEVEL1 + if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(CMD, "mbt_drvdbg=0x%x\n", data); + mbt_drvdbg = data; + } + } +#endif + else if (!strncmp(prop->name, "init_cmds", strlen("init_cmds"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + init_cmds = (char *)string_data; + PRINTM(CMD, "init_cmds=%s\n", init_cmds); + } + } else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + init_cfg = (char *)string_data; + PRINTM(CMD, "init_cfg=%s\n", init_cfg); + } + } else if (!strncmp + (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg_ext = (char *)string_data; + PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext); + } + } else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg = (char *)string_data; + PRINTM(CMD, "cal_cfg=%s\n", cal_cfg); + } + } else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + bt_mac = (char *)string_data; + PRINTM(CMD, "bt_mac=%s\n", bt_mac); + } + } else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btindrst = data; + PRINTM(CMD, "btindrst=%d\n", btindrst); + } + } else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btpmic = data; + PRINTM(CMD, "btpmic=%d\n", btpmic); + } + } + } + LEAVE(); + return; +} +#endif + +#ifdef BLE_WAKEUP +/** + * @brief This function send getting whitelist cmd to FW + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_get_whitelist_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_WHITELIST); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(MSG, "Queue get whitelist Command(0x%x):%d\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]); + + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Get BLE_GET_WHITELIST: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_GET_WHITELIST); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function send whitelist cmd to FW + * + * @param priv A pointer to bt_private structure + * @param is_shutdown indicate shutdown mode + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_send_whitelist_cmd(bt_private *priv, bool is_shutdown) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + u8 count = 0, i = 0; + BT_CMD *pcmd; + ENTER(); + + count = priv->white_list[0]; + for (i = 0; (i < count) && (i < 10); i++) { + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((HCI_BLE_GRP_BLE_CMDS << 10) | + HCI_BLE_ADD_DEV_TO_WHITELIST_OCF); + pcmd->length = 7; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + pcmd->data[0] = 0; + memcpy(&pcmd->data[1], &priv->white_list[1 + i * 6], 6); + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(MSG, "Queue send whitelist Command(0x%x):%d\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]); + + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + priv->adapter->wait_event_timeout = is_shutdown; + wake_up_interruptible(&priv->MainThread.waitQ); + if (is_shutdown) { + if (!os_wait_timeout + (priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + priv->adapter->wait_event_timeout = FALSE; + PRINTM(ERROR, + "BT: Get BLE_GET_WHITELIST: timeout:\n"); + bt_cmd_timeout_func(priv, + HCI_BLE_ADD_DEV_TO_WHITELIST_OCF); + } + } else { + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, + "BT: Get BLE_GET_WHITELIST: timeout:\n"); + bt_cmd_timeout_func(priv, + HCI_BLE_ADD_DEV_TO_WHITELIST_OCF); + } + } + } +exit: + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function initializes the adapter structure + * and set default value to the member of adapter. + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +static void +bt_init_adapter(bt_private *priv) +{ + ENTER(); +#ifdef CONFIG_OF + bt_init_from_dev_tree(); +#endif + skb_queue_head_init(&priv->adapter->tx_queue); + skb_queue_head_init(&priv->adapter->pending_queue); + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + priv->adapter->fwdump_fname = NULL; + init_waitqueue_head(&priv->adapter->cmd_wait_q); + LEAVE(); +} + +/** + * @brief This function initializes firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (fw == 0) { + sbi_enable_host_int(priv); + goto done; + } + sbi_disable_host_int(priv); + if ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + priv->fw_crc_check = fw_crc_check; + if (sbi_download_fw(priv)) { + PRINTM(ERROR, " FW failed to be download!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +#define FW_POLL_TRIES 100 +#define SD8897_FW_RESET_REG 0x0E8 +#define SD8887_FW_RESET_REG 0x0B6 +#define SD8977_SD8978_SD8997_FW_RESET_REG 0x0EE +#define SD8887_SD8897_FW_RESET_VAL 1 +#define SD8977_SD8978_SD8997_FW_RESET_VAL 0x99 + +/** + * @brief This function reload firmware + * + * @param priv A pointer to bt_private + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +static int +bt_reload_fw(bt_private *priv, int mode) +{ + int ret = 0, tries = 0; + u8 value = 1; + u32 reset_reg = 0; + u8 reset_val = 0; + + ENTER(); + if ((mode != FW_RELOAD_SDIO_INBAND_RESET) && + (mode != FW_RELOAD_NO_EMULATION)) { + PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode); + return -EFAULT; + } + + /** flush pending tx_queue */ + skb_queue_purge(&priv->adapter->tx_queue); + if (mode == FW_RELOAD_SDIO_INBAND_RESET) { + if (priv->card_type == CARD_TYPE_SD8887) { + reset_reg = SD8887_FW_RESET_REG; + reset_val = SD8887_SD8897_FW_RESET_VAL; + } else if (priv->card_type == CARD_TYPE_SD8897) { + reset_reg = SD8897_FW_RESET_REG; + reset_val = SD8887_SD8897_FW_RESET_VAL; + } else if ((priv->card_type == CARD_TYPE_SD8977) || + (priv->card_type == CARD_TYPE_SD8997) || + (priv->card_type == CARD_TYPE_SD8978) || + (priv->card_type == CARD_TYPE_SD8987)) { + reset_reg = SD8977_SD8978_SD8997_FW_RESET_REG; + reset_val = SD8977_SD8978_SD8997_FW_RESET_VAL; + } + sbi_disable_host_int(priv); + /** Wake up firmware firstly */ + sbi_wakeup_firmware(priv); + + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = sd_write_reg(priv, reset_reg, 0xba); + if (!ret) { + ret = sd_read_reg(priv, reset_reg, &value); + if (!ret && (value == 0xba)) { + PRINTM(MSG, "Fw wake up\n"); + break; + } + } + udelay(1000); + } + + ret = sd_write_reg(priv, reset_reg, reset_val); + if (ret) { + PRINTM(ERROR, "Failed to write register.\n"); + goto done; + } + + /** Poll register around 1 ms */ + for (; tries < FW_POLL_TRIES; ++tries) { + ret = sd_read_reg(priv, reset_reg, &value); + if (ret) { + PRINTM(ERROR, "Failed to read register.\n"); + goto done; + } + if (value == 0) + /** FW is ready */ + break; + udelay(1000); + } + if (value) { + PRINTM(ERROR, + "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + } + + sbi_enable_host_int(priv); + /** reload FW */ + ret = bt_init_fw(priv); + if (ret) { + PRINTM(ERROR, "Re download firmware failed.\n"); + ret = -EFAULT; + } + LEAVE(); + return ret; +done: + sbi_enable_host_int(priv); + LEAVE(); + return ret; +} + +/** + * @brief This function request to reload firmware + * + * @param priv A pointer to bt_private + * @param mode fw reload mode. + * + * @return N/A + */ +void +bt_request_fw_reload(bt_private *priv, int mode) +{ + ENTER(); + if (mode == FW_RELOAD_WITH_EMULATION) { + bt_fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MSG, "BT: FW reload with re-emulation...\n"); + LEAVE(); + return; + } + /** Reload FW */ + priv->fw_reload = TRUE; + if (bt_reload_fw(priv, mode)) { + PRINTM(ERROR, "FW reload fail\n"); + goto done; + } + priv->fw_reload = FALSE; + /** Other operation here? */ +done: + LEAVE(); + return; +} + +/** + * @brief This function frees the structure of adapter + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_free_adapter(bt_private *priv) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + kfree(adapter->tx_buffer); + kfree(adapter->hw_regs_buf); + /* Free allocated memory for fwdump filename */ + if (adapter->fwdump_fname) { + kfree(adapter->fwdump_fname); + adapter->fwdump_fname = NULL; + } + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; + + LEAVE(); +} + +/** + * @brief This function handles the wrapper_dev ioctl + * + * @param hev A pointer to wrapper_dev structure + * @cmd ioctl cmd + * @arg argument + * @return -ENOIOCTLCMD + */ +static int +mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg) +{ + bt_private *priv = NULL; + int ret = 0; +#ifdef BLE_WAKEUP + u16 len; +#endif + + ENTER(); + + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n"); + ret = -ENODEV; + goto done; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n", + m_dev->flags); + ret = -EBUSY; + goto done; + } + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { +#ifdef BLE_WAKEUP + case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM: + PRINTM(MSG, "BT: Set ble wakeup parameters\n"); + if (copy_from_user(&len, arg, sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params length\n"); + ret = -EFAULT; + goto done; + } + /** Convert little endian length */ + len = __le16_to_cpu(len); + if (len < 2) { + PRINTM(ERROR, + "BT_IOCTL: Invalid ble wakeup params len %d\n", + len); + ret = -EFAULT; + goto done; + } + if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + priv->ble_wakeup_buf = + kzalloc(len + sizeof(u16), GFP_KERNEL); + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t" + "for ble wakeup parameters\n"); + ret = -ENOMEM; + goto done; + } + priv->ble_wakeup_buf_size = len + sizeof(u16); + } + if (copy_from_user + (priv->ble_wakeup_buf, arg, len + sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf, + len + sizeof(u16)); + break; + case MBTCHAR_IOCTL_BLE_GET_WHITELIST: + bt_get_whitelist_cmd(priv); + DBG_HEXDUMP(DAT_D, "white_list:", priv->white_list, + sizeof(priv->white_list)); + break; +#endif + default: + break; + } + +done: +#ifdef BLE_WAKEUP + if (ret && priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + LEAVE(); + return ret; +} + +/** + * @brief This function handles wrapper device destruct + * + * @param m_dev A pointer to m_dev structure + * + * @return N/A + */ +static void +mdev_destruct(struct m_dev *m_dev) +{ + ENTER(); + LEAVE(); + return; +} + +/** + * @brief This function handles the wrapper device transmit + * + * @param m_dev A pointer to m_dev structure + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb) +{ + bt_private *priv = NULL; + + ENTER(); + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n"); + LEAVE(); + return -ENODEV; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n", + m_dev->flags); + LEAVE(); + return -EBUSY; + } + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + m_dev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + m_dev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + m_dev->stat.sco_tx++; + break; + } + + if (m_dev->dev_type == DEBUG_TYPE) { + /* remember the ogf_ocf */ + priv->debug_device_pending = 1; + priv->debug_ocf_ogf[0] = skb->data[0]; + priv->debug_ocf_ogf[1] = skb->data[1]; + PRINTM(CMD, "debug_ocf_ogf[0]=0x%x debug_ocf_ogf[1]=0x%x\n", + priv->debug_ocf_ogf[0], priv->debug_ocf_ogf[1]); + } + + if (priv->adapter->tx_lock == TRUE) + skb_queue_tail(&priv->adapter->pending_queue, skb); + else + bt_queue_frame(priv, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function flushes the transmit queue + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_flush(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->pending_queue); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function closes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_close(struct m_dev *m_dev) +{ + + ENTER(); + mdev_req_lock(m_dev); + if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + + if (m_dev->flush) + m_dev->flush(m_dev); + /* wait up pending read and unregister char dev */ + wake_up_interruptible(&m_dev->req_wait_q); + /* Drop queues */ + skb_queue_purge(&m_dev->rx_q); + if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + module_put(THIS_MODULE); + m_dev->flags = 0; + mdev_req_unlock(m_dev); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function opens the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +static int +mdev_open(struct m_dev *m_dev) +{ + ENTER(); + + if (try_module_get(THIS_MODULE) == 0) + return BT_STATUS_FAILURE; + + set_bit(HCI_RUNNING, &m_dev->flags); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function queries the wrapper device + * + * @param m_dev A pointer to m_dev structure + * @param arg arguement + * + * @return BT_STATUS_SUCCESS or other + */ +void +mdev_query(struct m_dev *m_dev, void *arg) +{ + struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer; + + ENTER(); + if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type))) + PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n"); + + LEAVE(); +} + +/** + * @brief This function initializes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +void +init_m_dev(struct m_dev *m_dev) +{ + m_dev->dev_pointer = NULL; + m_dev->driver_data = NULL; + m_dev->dev_type = 0; + m_dev->spec_type = 0; + skb_queue_head_init(&m_dev->rx_q); + init_waitqueue_head(&m_dev->req_wait_q); + init_waitqueue_head(&m_dev->rx_wait_q); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + init_MUTEX(&m_dev->req_lock); +#else + sema_init(&m_dev->req_lock, 1); +#endif + spin_lock_init(&m_dev->rxlock); + memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats)); + m_dev->open = mdev_open; + m_dev->close = mdev_close; + m_dev->flush = mdev_flush; + m_dev->send = mdev_send_frame; + m_dev->destruct = mdev_destruct; + m_dev->ioctl = mdev_ioctl; + m_dev->query = mdev_query; + m_dev->owner = THIS_MODULE; + +} + +/** + * @brief This function handles the major job in bluetooth driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to bt_thread structure + * @return BT_STATUS_SUCCESS + */ +static int +bt_service_main_thread(void *data) +{ + bt_thread *thread = data; + bt_private *priv = thread->priv; + bt_adapter *adapter = priv->adapter; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + wait_queue_t wait; +#else + wait_queue_entry_t wait; +#endif + struct sk_buff *skb; + ENTER(); + bt_activate_thread(thread); + init_waitqueue_entry(&wait, current); + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&thread->waitQ, &wait); + OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE); + if (priv->adapter->WakeupTries || + ((!priv->adapter->IntCounter) && + (!priv->bt_dev.tx_dnld_rdy || + skb_queue_empty(&priv->adapter->tx_queue)) + )) { + PRINTM(INFO, "Main: Thread sleeping...\n"); + schedule(); + } + OS_SET_THREAD_STATE(TASK_RUNNING); + remove_wait_queue(&thread->waitQ, &wait); + if (kthread_should_stop() || adapter->SurpriseRemoved) { + PRINTM(INFO, "main-thread: break from main thread: " + "SurpriseRemoved=0x%x\n", + adapter->SurpriseRemoved); + break; + } + + PRINTM(INFO, "Main: Thread waking up...\n"); + + if (priv->adapter->IntCounter) { + OS_INT_DISABLE; + adapter->IntCounter = 0; + OS_INT_RESTORE; + sbi_get_int_status(priv); + } else if ((priv->adapter->ps_state == PS_SLEEP) && + (!skb_queue_empty(&priv->adapter->tx_queue) + )) { + priv->adapter->WakeupTries++; + sbi_wakeup_firmware(priv); + continue; + } + if (priv->adapter->ps_state == PS_SLEEP) + continue; + if (priv->bt_dev.tx_dnld_rdy == TRUE) { + if (!skb_queue_empty(&priv->adapter->tx_queue)) { + skb = skb_dequeue(&priv->adapter->tx_queue); + if (skb) + send_single_packet(priv, skb); + } + } + } + bt_deactivate_thread(thread); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param m_dev A pointer to m_dev structure + * @return N/A + */ +void +bt_interrupt(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + if (!priv || !priv->adapter) { + LEAVE(); + return; + } + PRINTM(INTR, "*\n"); + priv->adapter->ps_state = PS_AWAKE; + if (priv->adapter->hs_state == HS_ACTIVATED) { + PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name); + priv->adapter->hs_state = HS_DEACTIVATED; + } + priv->adapter->WakeupTries = 0; + priv->adapter->IntCounter++; + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); +} + +/** + * @brief Dynamic release of char dev + * + * @param kobj A pointer to kobject structure + * + * @return N/A + */ +static void +char_dev_release_dynamic(struct kobject *kobj) +{ + struct char_dev *cdev = container_of(kobj, struct char_dev, kobj); + ENTER(); + PRINTM(INFO, "free char_dev\n"); + kfree(cdev); + LEAVE(); +} + +static struct kobj_type ktype_char_dev_dynamic = { + .release = char_dev_release_dynamic, +}; + +/** + * @brief Allocation of char dev + * + * @param N/A + * + * @return char_dev + */ +static struct char_dev * +alloc_char_dev(void) +{ + struct char_dev *cdev; + ENTER(); + cdev = kzalloc(sizeof(struct char_dev), GFP_KERNEL); + if (cdev) { + kobject_init(&cdev->kobj, &ktype_char_dev_dynamic); + PRINTM(INFO, "alloc char_dev\n"); + } + return cdev; +} + +/** + * @brief Dynamic release of bt private + * + * @param kobj A pointer to kobject structure + * + * @return N/A + */ +static void +bt_private_dynamic_release(struct kobject *kobj) +{ + bt_private *priv = container_of(kobj, bt_private, kobj); + ENTER(); + PRINTM(INFO, "free bt priv\n"); + kfree(priv); + LEAVE(); +} + +static struct kobj_type ktype_bt_private_dynamic = { + .release = bt_private_dynamic_release, +}; + +/** + * @brief Allocation of bt private + * + * @param N/A + * + * @return bt_private + */ +static bt_private * +bt_alloc_priv(void) +{ + bt_private *priv; + ENTER(); + priv = kzalloc(sizeof(bt_private), GFP_KERNEL); + if (priv) { + kobject_init(&priv->kobj, &ktype_bt_private_dynamic); + PRINTM(INFO, "alloc bt priv\n"); + } + LEAVE(); + return priv; +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return kobject structure + */ +struct kobject * +bt_priv_get(bt_private *priv) +{ + PRINTM(INFO, "bt priv get object"); + return kobject_get(&priv->kobj); +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return N/A + */ +void +bt_priv_put(bt_private *priv) +{ + PRINTM(INFO, "bt priv put object"); + kobject_put(&priv->kobj); +} + +/** + * @brief This function send init commands to firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_init_cmd(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + if (ret < 0) { + PRINTM(FATAL, "Module cfg command send failed!\n"); + goto done; + } + PRINTM(MSG, "BT: btindrst=0x%x\n", btindrst); + if (btindrst != -1) { + ret = bt_set_independent_reset(priv); + if (ret < 0) { + PRINTM(FATAL, "Independent reset failed!\n"); + goto done; + } + } + if (btpmic + && ((priv->card_type == CARD_TYPE_SD8997) || + (priv->card_type == CARD_TYPE_SD8977) || + (priv->card_type == CARD_TYPE_SD8978)) + ) { + if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) { + PRINTM(FATAL, "BT: PMIC Configure failed \n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE); + if (ret < 0) { + PRINTM(FATAL, "%s BLE deepsleep failed!\n", + deep_sleep ? "Enable" : "Disable"); + goto done; + } + if (psmode) { + priv->bt_dev.psmode = TRUE; + priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME; + ret = bt_enable_ps(priv); + if (ret < 0) { + PRINTM(FATAL, "Enable PS mode failed!\n"); + goto done; + } + } +#if defined(SDIO_SUSPEND_RESUME) + priv->bt_dev.gpio_gap = DEF_GPIO_GAP; + ret = bt_send_hscfg_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "Send HSCFG failed!\n"); + goto done; + } +#endif + priv->bt_dev.sdio_pull_cfg = 0xffffffff; + priv->bt_dev.sdio_pull_ctrl = 0; + wake_up_interruptible(&priv->MainThread.waitQ); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reinit firmware after redownload firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_reinit_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + /* block all the packet from bluez */ + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) + priv->adapter->tx_lock = TRUE; + + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) { + priv->adapter->tx_lock = FALSE; + bt_restore_tx_queue(priv); + } + + if (init_cmds) { + if (BT_STATUS_SUCCESS != bt_init_cmds(priv, init_cmds)) { + PRINTM(FATAL, "BT: Set user init commands failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); +done: + return ret; +} + +/** + * @brief Module configuration and register device + * + * @param priv A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_conf_dpc(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct mbt_dev *mbt_dev = NULL; + struct debug_dev *debug_dev = NULL; + int i = 0; + struct char_dev *char_dev = NULL; + char dev_file[DEV_NAME_LEN + 5]; + unsigned char dev_type = 0; + + ENTER(); + + priv->bt_dev.tx_dnld_rdy = TRUE; + if (priv->fw_reload) { + bt_reinit_fw(priv); + LEAVE(); + return ret; + } + + if (drv_mode & DRV_MODE_BT) { + mbt_dev = alloc_mbt_dev(); + if (!mbt_dev) { + PRINTM(FATAL, "Can not allocate mbt dev\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + init_m_dev(&(priv->bt_dev.m_dev[BT_SEQ])); + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE; + priv->bt_dev.m_dev[BT_SEQ].spec_type = IANYWHERE_SPEC; + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)mbt_dev; + priv->bt_dev.m_dev[BT_SEQ].driver_data = priv; + } + + dev_type = HCI_SDIO; + + if (mbt_dev) + mbt_dev->type = dev_type; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + + if (mbt_dev && priv->bt_dev.devType == DEV_TYPE_AMP) { + mbt_dev->type |= HCI_BT_AMP; + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_AMP_TYPE; + } + /** Process device tree init parameters before register hci device. + * Since uplayer device has not yet registered, no need to block tx queue. + * */ + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } else if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (init_cmds) { + if (BT_STATUS_SUCCESS != bt_init_cmds(priv, init_cmds)) { + PRINTM(FATAL, "BT: Set user init commands failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + /* Get FW version */ + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); + + if (mbt_dev) { + /** init mbt_dev */ + mbt_dev->flags = 0; + mbt_dev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); + mbt_dev->esco_type = (ESCO_HV1); + mbt_dev->link_mode = (HCI_LM_ACCEPT); + + mbt_dev->idle_timeout = 0; + mbt_dev->sniff_max_interval = 800; + mbt_dev->sniff_min_interval = 80; + for (i = 0; i < 3; i++) + mbt_dev->reassembly[i] = NULL; + atomic_set(&mbt_dev->promisc, 0); + + /** alloc char dev node */ + char_dev = alloc_char_dev(); + if (!char_dev) { + class_destroy(chardev_class); + ret = -ENOMEM; + goto err_kmalloc; + } + char_dev->minor = MBTCHAR_MINOR_BASE + mbtchar_minor; + if (mbt_dev->type & HCI_BT_AMP) + char_dev->dev_type = BT_AMP_TYPE; + else + char_dev->dev_type = BT_TYPE; + + if (bt_name) + snprintf(mbt_dev->name, sizeof(mbt_dev->name), "%s%d", + bt_name, mbtchar_minor); + else + snprintf(mbt_dev->name, sizeof(mbt_dev->name), + "mbtchar%d", mbtchar_minor); + snprintf(dev_file, sizeof(dev_file), "/dev/%s", mbt_dev->name); + mbtchar_minor++; + PRINTM(MSG, "BT: Create %s\n", dev_file); + + /** register m_dev to BT char device */ + priv->bt_dev.m_dev[BT_SEQ].index = char_dev->minor; + char_dev->m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + + /** create BT char device node */ + register_char_dev(char_dev, chardev_class, MODULE_NAME, + mbt_dev->name); + + /** chmod & chown for BT char device */ + mbtchar_chown(dev_file, AID_SYSTEM, AID_NET_BT_STACK); + mbtchar_chmod(dev_file, 0666); + + /** create proc device */ + snprintf(priv->bt_dev.m_dev[BT_SEQ].name, + sizeof(priv->bt_dev.m_dev[BT_SEQ].name), + mbt_dev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ); + } + + if ((debug_intf) && ((drv_mode & DRV_MODE_BT) + )) { + /** alloc debug_dev */ + debug_dev = alloc_debug_dev(); + if (!debug_dev) { + PRINTM(FATAL, "Can not allocate debug dev\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + + /** init m_dev */ + init_m_dev(&(priv->bt_dev.m_dev[DEBUG_SEQ])); + priv->bt_dev.m_dev[DEBUG_SEQ].dev_type = DEBUG_TYPE; + priv->bt_dev.m_dev[DEBUG_SEQ].spec_type = GENERIC_SPEC; + priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = (void *)debug_dev; + priv->bt_dev.m_dev[DEBUG_SEQ].driver_data = priv; + + /** create char device for Debug */ + char_dev = alloc_char_dev(); + if (!char_dev) { + class_destroy(chardev_class); + ret = -ENOMEM; + goto err_kmalloc; + } + char_dev->minor = DEBUGCHAR_MINOR_BASE + debugchar_minor; + char_dev->dev_type = DEBUG_TYPE; + if (debug_name) + snprintf(debug_dev->name, sizeof(debug_dev->name), + "%s%d", debug_name, debugchar_minor); + else + snprintf(debug_dev->name, sizeof(debug_dev->name), + "mdebugchar%d", debugchar_minor); + snprintf(dev_file, sizeof(dev_file), "/dev/%s", + debug_dev->name); + PRINTM(MSG, "BT: Create %s\n", dev_file); + debugchar_minor++; + + /** register char dev */ + priv->bt_dev.m_dev[DEBUG_SEQ].index = char_dev->minor; + char_dev->m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + register_char_dev(char_dev, chardev_class, MODULE_NAME, + debug_dev->name); + + /** chmod for debug char device */ + mbtchar_chmod(dev_file, 0666); + + /** create proc device */ + snprintf(priv->bt_dev.m_dev[DEBUG_SEQ].name, + sizeof(priv->bt_dev.m_dev[DEBUG_SEQ].name), + debug_dev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[DEBUG_SEQ]), DEBUG_SEQ); + } + +done: + LEAVE(); + return ret; +err_kmalloc: + LEAVE(); + return ret; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the bt_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to bt_private structure + */ + +bt_private * +bt_add_card(void *card) +{ + bt_private *priv = NULL; + int index = 0; + + ENTER(); + + priv = bt_alloc_priv(); + if (!priv) { + PRINTM(FATAL, "Can not allocate priv\n"); + LEAVE(); + return NULL; + } + /* Save the handle */ + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == NULL) + break; + } + if (index < MAX_BT_ADAPTER) { + m_priv[index] = priv; + } else { + PRINTM(ERROR, "Exceeded maximum cards supported!\n"); + goto err_kmalloc; + } + /* allocate buffer for bt_adapter */ + priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL); + if (!priv->adapter) { + PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n"); + goto err_kmalloc; + } + priv->adapter->tx_buffer = + kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->tx_buffer) { + PRINTM(FATAL, "Allocate buffer for transmit\n"); + goto err_kmalloc; + } + priv->adapter->tx_buf = + (u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT); + priv->adapter->hw_regs_buf = + kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->hw_regs_buf) { + PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n"); + goto err_kmalloc; + } + priv->adapter->hw_regs = + (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT); + bt_init_adapter(priv); + + PRINTM(INFO, "Starting kthread...\n"); + priv->MainThread.priv = priv; + spin_lock_init(&priv->driver_lock); + + bt_create_thread(bt_service_main_thread, &priv->MainThread, + "bt_main_service"); + + /* wait for mainthread to up */ + while (!priv->MainThread.pid) + os_sched_timeout(1); + + sdio_update_card_type(priv, card); + /* Update driver version */ + if (priv->card_type == CARD_TYPE_SD8787) + memcpy(mbt_driver_version, CARD_SD8787, strlen(CARD_SD8787)); + else if (priv->card_type == CARD_TYPE_SD8777) + memcpy(mbt_driver_version, CARD_SD8777, strlen(CARD_SD8777)); + else if (priv->card_type == CARD_TYPE_SD8887) + memcpy(mbt_driver_version, CARD_SD8887, strlen(CARD_SD8887)); + else if (priv->card_type == CARD_TYPE_SD8897) + memcpy(mbt_driver_version, CARD_SD8897, strlen(CARD_SD8897)); + else if (priv->card_type == CARD_TYPE_SD8797) + memcpy(mbt_driver_version, CARD_SD8797, strlen(CARD_SD8797)); + else if (priv->card_type == CARD_TYPE_SD8977) + memcpy(mbt_driver_version, CARD_SD8977, strlen(CARD_SD8977)); + else if (priv->card_type == CARD_TYPE_SD8978) + memcpy(mbt_driver_version, CARD_SD8978, strlen(CARD_SD8978)); + else if (priv->card_type == CARD_TYPE_SD8997) + memcpy(mbt_driver_version, CARD_SD8997, strlen(CARD_SD8997)); + else if (priv->card_type == CARD_TYPE_SD8987) + memcpy(mbt_driver_version, CARD_SD8987, strlen(CARD_SD8987)); + + if (BT_STATUS_SUCCESS != sdio_get_sdio_device(priv)) + goto err_kmalloc; + + /** user config file */ + init_waitqueue_head(&priv->init_user_conf_wait_q); + + priv->bt_dev.card = card; + + ((struct sdio_mmc_card *)card)->priv = priv; + priv->adapter->sd_ireg = 0; + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (sbi_register_dev(priv) < 0) { + PRINTM(FATAL, "Failed to register bt device!\n"); + goto err_registerdev; + } + if (bt_init_fw(priv)) { + PRINTM(FATAL, "BT Firmware Init Failed\n"); + goto err_init_fw; + } + LEAVE(); + return priv; + +err_init_fw: + clean_up_m_devs(priv); + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); +err_registerdev: + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); +err_kmalloc: + if (priv->adapter) + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return NULL; +} + +/** + * @brief This function send hardware remove event + * + * @param priv A pointer to bt_private + * @return N/A + */ +void +bt_send_hw_remove_event(bt_private *priv) +{ + struct sk_buff *skb = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + ENTER(); + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_HARDWARE_REMOVE 0x24 + skb = bt_skb_alloc(3, GFP_ATOMIC); + skb->data[0] = HCI_HARDWARE_ERROR_EVT; + skb->data[1] = 1; + skb->data[2] = HCI_HARDWARE_REMOVE; + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 3); + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + PRINTM(MSG, "Send HW ERROR event\n"); + if (!mdev_recv_frame(skb)) { +#define RX_WAIT_TIMEOUT 300 + mdev_bt->wait_rx_complete = TRUE; + mdev_bt->rx_complete_flag = FALSE; + if (os_wait_interruptible_timeout + (mdev_bt->rx_wait_q, mdev_bt->rx_complete_flag, + RX_WAIT_TIMEOUT)) + PRINTM(MSG, "BT stack received the event\n"); + mdev_bt->stat.byte_rx += 3; + } + } + LEAVE(); + return; +} + +#ifdef BLE_WAKEUP +/** + * @brief This function used to config BLE wakeup pattern + * + * @param priv A pointer to bt_private structure + * @param is_shutdown indicate shutdown mode + * @return N/A + */ +int +bt_config_ble_wakeup(bt_private *priv, bool is_shutdown) +{ + int ret = BT_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + u16 ocf = 0, left_len; + u8 len, more_cmd; + u8 *pos; + + ENTER(); + + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT: no ble wakeup parameters found\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Config ble wakeup pattern\n"); + + pos = priv->ble_wakeup_buf; + left_len = *(u16 *) pos; + left_len = __le16_to_cpu(left_len); + pos += sizeof(u16); + + while (left_len >= 2) { + more_cmd = *pos; + len = *(pos + 1); + if (((len + 2) > left_len) || + (!more_cmd && ((len + 2) < left_len))) { + PRINTM(ERROR, "Invalid ble parameters\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (ocf == BT_CMD_ENABLE_WRITE_SCAN) + bt_send_whitelist_cmd(priv, is_shutdown); + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy(skb_put(skb, len), pos + 2, len); + bt_cb(skb)->pkt_type = *(u8 *)skb->data; + skb_pull(skb, 1); + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data; + ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode); + priv->adapter->cmd_complete = FALSE; + priv->adapter->wait_event_timeout = is_shutdown; + + wake_up_interruptible(&priv->MainThread.waitQ); + if (is_shutdown) { + if (!os_wait_timeout + (priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + priv->adapter->wait_event_timeout = FALSE; + PRINTM(ERROR, + "BT: Set Set ble wakeup cmd 0x%x timeout:\n", + priv->bt_dev.send_cmd_opcode); + bt_cmd_timeout_func(priv, ocf); + goto done; + } + } else { + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, + "BT: Set Set ble wakeup cmd 0x%x timeout:\n", + priv->bt_dev.send_cmd_opcode); + bt_cmd_timeout_func(priv, ocf); + goto done; + } + } + + pos += len + 2; + left_len -= len + 2; + } + +done: + if (ret != BT_STATUS_SUCCESS) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function send system suspend event + * + * @param priv A pointer to bt_private + * @return BT_STATUS_SUCCESS + */ +int +bt_send_system_event(bt_private *priv, u8 flag) +{ + struct sk_buff *skb = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + + ENTER(); + + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return BT_STATUS_FAILURE; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + + skb = bt_skb_alloc(4, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "Fail to allocate sys suspend event skb\n"); + return BT_STATUS_FAILURE; + } + skb->data[0] = VENDOR_SPECIFIC_EVENT; + skb->data[1] = 2; + skb->data[2] = HCI_SYSTEM_SUSPEND_EVT; + if (flag) + skb->data[3] = HCI_SYSTEM_SUSPEND; + else + skb->data[3] = HCI_SYSTEM_RESUME; + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 4); + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + PRINTM(MSG, "Send system %s event\n", + flag ? "suspend" : "resume"); + if (!mdev_recv_frame(skb)) { +#define RX_WAIT_TIMEOUT 300 + mdev_bt->wait_rx_complete = TRUE; + mdev_bt->rx_complete_flag = FALSE; + if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q, + mdev_bt-> + rx_complete_flag, + RX_WAIT_TIMEOUT)) + PRINTM(MSG, "BT stack received the event\n"); + mdev_bt->stat.byte_rx += 4; + } + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * @return BT_STATUS_SUCCESS + */ +int +bt_remove_card(void *card) +{ + bt_private *priv = (bt_private *)card; + int index; + ENTER(); + if (!priv) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv->adapter->SurpriseRemoved = TRUE; + + bt_send_hw_remove_event(priv); +#ifdef BLE_WAKEUP + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + wake_up_interruptible(&priv->adapter->cmd_wait_q); + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) { + os_sched_timeout(1); + wake_up_interruptible(&priv->MainThread.waitQ); + } + + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); + clean_up_m_devs(priv); + PRINTM(INFO, "Free Adapter\n"); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function initializes module. + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_module(void) +{ + int ret = BT_STATUS_SUCCESS; + int index; + ENTER(); + PRINTM(MSG, "BT: Loading driver\n"); + /* Init the bt_private pointer array first */ + for (index = 0; index < MAX_BT_ADAPTER; index++) + m_priv[index] = NULL; + bt_root_proc_init(); + + /** create char device class */ + chardev_class = class_create(THIS_MODULE, MODULE_NAME); + if (IS_ERR(chardev_class)) { + PRINTM(ERROR, "Unable to allocate class\n"); + bt_root_proc_remove(); + ret = PTR_ERR(chardev_class); + goto done; + } + + if (sbi_register() == NULL) { + bt_root_proc_remove(); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + if (ret) + PRINTM(MSG, "BT: Driver loading failed\n"); + else + PRINTM(MSG, "BT: Driver loaded successfully\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void +bt_exit_module(void) +{ + bt_private *priv; + int index; + ENTER(); + PRINTM(MSG, "BT: Unloading driver\n"); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + priv = m_priv[index]; + if (!priv) + continue; + if (priv && !priv->adapter->SurpriseRemoved) { + if (BT_STATUS_SUCCESS == bt_send_reset_command(priv)) + bt_send_module_cfg_cmd(priv, + MODULE_SHUTDOWN_REQ); + } + sbi_disable_host_int(priv); + + } + + sbi_unregister(); + + bt_root_proc_remove(); + class_destroy(chardev_class); + PRINTM(MSG, "BT: Driver unloaded\n"); + LEAVE(); +} + +module_init(bt_init_module); +module_exit(bt_exit_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +module_param(fw, int, 0); +MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware"); +module_param(fw_crc_check, int, 0); +MODULE_PARM_DESC(fw_crc_check, + "1: Enable FW download CRC check (default); 0: Disable FW download CRC check"); +module_param(psmode, int, 0); +MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode"); +module_param(deep_sleep, int, 0); +MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep"); +#ifdef CONFIG_OF +module_param(dts_enable, int, 0); +MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS"); +#endif +#ifdef DEBUG_LEVEL1 +module_param(mbt_drvdbg, uint, 0); +MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL"); +#endif +#ifdef SDIO_SUSPEND_RESUME +module_param(mbt_pm_keep_power, int, 0); +MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power"); +#endif +module_param(init_cfg, charp, 0); +MODULE_PARM_DESC(init_cfg, "BT init config file name"); +module_param(cal_cfg, charp, 0); +MODULE_PARM_DESC(cal_cfg, "BT calibrate file name"); +module_param(cal_cfg_ext, charp, 0); +MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name"); +module_param(bt_mac, charp, 0660); +MODULE_PARM_DESC(bt_mac, "BT init mac address"); +module_param(init_cmds, charp, 0); +MODULE_PARM_DESC(init_cmds, "BT init commands file name"); +module_param(drv_mode, int, 0); +MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;"); +module_param(bt_name, charp, 0); +MODULE_PARM_DESC(bt_name, "BT interface name"); +module_param(debug_intf, int, 0); +MODULE_PARM_DESC(debug_intf, + "1: Enable debug interface; 0: Disable debug interface "); +module_param(debug_name, charp, 0); +MODULE_PARM_DESC(debug_name, "Debug interface name"); +module_param(bt_fw_reload, int, 0); +MODULE_PARM_DESC(bt_fw_reload, + "0: disable fw_reload; 1: enable fw reload feature"); +module_param(btindrst, int, 0); +MODULE_PARM_DESC(btindrst, + "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset."); +module_param(btpmic, int, 0); +MODULE_PARM_DESC(btpmic, + "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)"); +module_param(bt_fw_serial, int, 0); +MODULE_PARM_DESC(bt_fw_serial, + "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8987/bt_char/bt_proc.c b/bt_sd8987/bt_char/bt_proc.c new file mode 100644 index 0000000..0264c94 --- /dev/null +++ b/bt_sd8987/bt_char/bt_proc.c
@@ -0,0 +1,723 @@ +/** @file bt_proc.c + * + * @brief This file handle the functions for proc files + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/proc_fs.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** proc diretory root */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#define PROC_DIR NULL +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#define PROC_DIR (&proc_root) +#else +#define PROC_DIR proc_net +#endif + +/** Proc mbt directory entry */ +static struct proc_dir_entry *proc_mbt; + +#define CMD52_STR_LEN 50 +static char cmd52_string[CMD52_STR_LEN]; + +/** proc data structure */ +struct proc_data { + /** Read length */ + int rdlen; + /** Read buffer */ + char *rdbuf; + /** Write length */ + int wrlen; + /** Maximum write length */ + int maxwrlen; + /** Write buffer */ + char *wrbuf; + /** Private structure */ + struct _bt_private *pbt; + void (*on_close) (struct inode *, struct file *); +}; + +/** Default file permission */ +#define DEFAULT_FILE_PERM 0644 + +/** Bluetooth device offset */ +#define OFFSET_BT_DEV 0x01 +/** Bluetooth adapter offset */ +#define OFFSET_BT_ADAPTER 0x02 +/** Show integer */ +#define SHOW_INT 0x10 +/** Show hex */ +#define SHOW_HEX 0x20 +/** Show string */ +#define SHOW_STRING 0x40 + +/** Device size */ +#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n) +/** Device address */ +#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n) + +/** Adapter size */ +#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n) +/** Adapter address */ +#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n) + +static struct item_data config_items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX} + , +#endif + {"idle_timeout", item_dev_size(idle_timeout), 0, + item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX} + , + {"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap), + OFFSET_BT_DEV | SHOW_HEX} + , + {"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0, + item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX} + , + {"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0, + item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT} + , + {"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode), + OFFSET_BT_DEV | SHOW_INT} + , + +}; + +static struct item_data status_items[] = { + {"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver), + OFFSET_BT_ADAPTER | SHOW_STRING}, + {"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0, + item_dev_addr(tx_dnld_rdy), + OFFSET_BT_DEV | SHOW_INT}, + {"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_state", item_adapter_size(hs_state), 0, + item_adapter_addr(hs_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"ps_state", item_adapter_size(ps_state), 0, + item_adapter_addr(ps_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"WakeupTries", item_adapter_size(WakeupTries), 0, + item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_recv", item_adapter_size(irq_recv), 0, + item_adapter_addr(irq_recv), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_done", item_adapter_size(irq_done), 0, + item_adapter_addr(irq_done), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"skb_pending", item_adapter_size(skb_pending), 0, + item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT}, +}; + +static struct item_data debug_items[] = { + {"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING}, +}; + +/** + * @brief convert string to number + * + * @param s pointer to numbered string + * @return converted number from string s + */ +int +string_to_number(char *s) +{ + int r = 0; + int base = 0; + int pn = 1; + + if (strncmp(s, "-", 1) == 0) { + pn = -1; + s++; + } + if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) { + base = 16; + s += 2; + } else + base = 10; + + for (s = s; *s != 0; s++) { + if ((*s >= '0') && (*s <= '9')) + r = (r * base) + (*s - '0'); + else if ((*s >= 'A') && (*s <= 'F')) + r = (r * base) + (*s - 'A' + 10); + else if ((*s >= 'a') && (*s <= 'f')) + r = (r * base) + (*s - 'a' + 10); + else + break; + } + + return r * pn; +} + +/** + * @brief Create cmd52 string + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +static int +form_cmd52_string(bt_private *priv) +{ + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X", + priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg, + priv->bt_dev.cmd52_val); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/* + * @brief Parse cmd52 string + * + * @param buffer A pointer user buffer + * @param len Length of user buffer + * @param func Parsed func number + * @param reg Parsed reg value + * @param val Parsed value to set + * @return BT_STATUS_SUCCESS + */ +static int +parse_cmd52_string(const char __user * buffer, size_t len, + int *func, int *reg, int *val) +{ + int ret = BT_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + + ENTER(); + + string = kzalloc(CMD52_STR_LEN, GFP_KERNEL); + if (!string) { + PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n"); + LEAVE(); + return -ENOMEM; + } + memcpy(string, buffer + strlen("sdcmd52rw="), + len - strlen("sdcmd52rw=")); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) + *func = string_to_number(pos); + + /* Get reg */ + pos = strsep(&string, " \t"); + if (pos) + *reg = string_to_number(pos); + + /* Get val (optional) */ + pos = strsep(&string, " \t"); + if (pos) + *val = string_to_number(pos); + kfree(string); + LEAVE(); + return ret; +} + +/** + * @brief This function handle generic proc file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS + */ +static int +proc_close(struct inode *inode, struct file *file) +{ + struct proc_data *pdata = file->private_data; + ENTER(); + if (pdata) { + if (pdata->on_close != NULL) + pdata->on_close(inode, file); + kfree(pdata->rdbuf); + kfree(pdata->wrbuf); + kfree(pdata); + } + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handle generic proc file read + * + * @param file A pointer to file structure + * @param buffer A pointer to output buffer + * @param len number of byte to read + * @param offset A pointer to offset of file + * @return number of output data + */ +static ssize_t +proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + if ((!pdata->rdbuf) || (pos < 0)) + return -EINVAL; + if (pos >= pdata->rdlen) + return 0; + if (len > pdata->rdlen - pos) + len = pdata->rdlen - pos; + if (copy_to_user(buffer, pdata->rdbuf + pos, len)) + return -EFAULT; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle generic proc file write + * + * @param file A pointer to file structure + * @param buffer A pointer to input buffer + * @param len number of byte to write + * @param offset A pointer to offset of file + * @return number of input data + */ +static ssize_t +proc_write(struct file *file, + const char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + int func = 0, reg = 0, val = 0; + int config_data = 0; + char *line = NULL; + + if (!pdata->wrbuf || (pos < 0)) + return -EINVAL; + if (pos >= pdata->maxwrlen) + return 0; + if (len > pdata->maxwrlen - pos) + len = pdata->maxwrlen - pos; + if (copy_from_user(pdata->wrbuf + pos, buffer, len)) + return -EFAULT; + if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) { + if (!strncmp + (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) { + line = pdata->wrbuf + pos; + line += strlen("fw_reload") + 1; + config_data = string_to_number(line); + } else + config_data = FW_RELOAD_SDIO_INBAND_RESET; + PRINTM(MSG, "Request fw_reload=%d\n", config_data); + bt_request_fw_reload(pdata->pbt, config_data); + } + if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) { + parse_cmd52_string(pdata->wrbuf + pos, len, &func, ®, &val); + sd_write_cmd52_val(pdata->pbt, func, reg, val); + } + if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) { + bt_dump_sdio_regs(pdata->pbt); + bt_dump_firmware_info_v2(pdata->pbt); + } + + if (pos + len > pdata->wrlen) + pdata->wrlen = len + file->f_pos; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle the generic file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return N/A + */ +static void +proc_on_close(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata = file->private_data; + char *line; + int i; + ENTER(); + if (!pdata->wrlen) + return; + line = pdata->wrbuf; + while (line[0]) { + for (i = 0; i < priv->num_items; i++) { + if (!strncmp + (line, priv->pdata[i].name, + strlen(priv->pdata[i].name))) { + line += strlen(priv->pdata[i].name) + 1; + if (priv->pdata[i].size == 1) + *((u8 *)priv->pdata[i].addr) = + (u8)string_to_number(line); + else if (priv->pdata[i].size == 2) + *((u16 *) priv->pdata[i].addr) = + (u16) string_to_number(line); + else if (priv->pdata[i].size == 4) + *((u32 *)priv->pdata[i].addr) = + (u32)string_to_number(line); + } + } + while (line[0] && line[0] != '\n') + line++; + if (line[0]) + line++; + } + if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd + || priv->pbt->bt_dev.sdio_pull_ctrl + || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) { + bt_prepare_command(priv->pbt); + wake_up_interruptible(&priv->pbt->MainThread.waitQ); + } + LEAVE(); + return; +} + +/** + * @brief This function handle the generic file open + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata; + int i; + char *p; + u32 val = 0; + ENTER(); + priv->pbt->adapter->skb_pending = + skb_queue_len(&priv->pbt->adapter->tx_queue); + file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL); + if (file->private_data == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n"); + LEAVE(); + return -ENOMEM; + } + pdata = (struct proc_data *)file->private_data; + pdata->pbt = priv->pbt; + pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL); + if (pdata->rdbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n"); + kfree(file->private_data); + LEAVE(); + return -ENOMEM; + } + if (priv->fileflag == DEFAULT_FILE_PERM) { + pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL); + if (pdata->wrbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n"); + kfree(pdata->rdbuf); + kfree(file->private_data); + return -ENOMEM; + } + pdata->maxwrlen = priv->bufsize; + pdata->on_close = proc_on_close; + } + p = pdata->rdbuf; + for (i = 0; i < priv->num_items; i++) { + if (priv->pdata[i].size == 1) + val = *((u8 *)priv->pdata[i].addr); + else if (priv->pdata[i].size == 2) + val = *((u16 *) priv->pdata[i].addr); + else if (priv->pdata[i].size == 4) + val = *((u32 *)priv->pdata[i].addr); + if (priv->pdata[i].flag & SHOW_INT) + p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_HEX) + p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_STRING) { + if (!strncmp + (priv->pdata[i].name, "sdcmd52rw", + strlen("sdcmd52rw"))) { + sd_read_cmd52_val(priv->pbt); + form_cmd52_string(priv->pbt); + } + p += sprintf(p, "%s=%s\n", priv->pdata[i].name, + (char *)priv->pdata[i].addr); + } + } + pdata->rdlen = strlen(pdata->rdbuf); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** Proc read ops */ +static const struct file_operations proc_read_ops = { + .read = proc_read, + .open = proc_open, + .release = proc_close +}; + +/** Proc Read-Write ops */ +static const struct file_operations proc_rw_ops = { + .read = proc_read, + .write = proc_write, + .open = proc_open, + .release = proc_close +}; + +static struct proc_private_data proc_files[] = { + {"status", S_IRUGO, 1024, + sizeof(status_items) / sizeof(status_items[0]), + &status_items[0], NULL, &proc_read_ops} + , + {"config", DEFAULT_FILE_PERM, 512, + sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL, + &proc_rw_ops} + , + {"debug", DEFAULT_FILE_PERM, 512, + sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL, + &proc_rw_ops} + , +}; + +/** + * @brief This function initializes proc entry + * + * @param priv A pointer to bt_private structure + * @param m_dev A pointer to struct m_dev + * @param seq Sequence number + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq) +{ + int ret = BT_STATUS_SUCCESS; + struct proc_dir_entry *entry; + int i, j; + + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + if (proc_mbt) { + priv->dev_proc[seq].proc_entry = + proc_mkdir(m_dev->name, proc_mbt); + if (!priv->dev_proc[seq].proc_entry) { + PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name); + ret = BT_STATUS_FAILURE; + goto done; + } + + priv->dev_proc[seq].pfiles = + kmalloc(sizeof(proc_files), GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles) { + PRINTM(ERROR, + "BT: Could not alloc memory for pfile!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files, + sizeof(proc_files)); + priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files); + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) + priv->dev_proc[seq].pfiles[j].pdata = NULL; + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + priv->dev_proc[seq].pfiles[j].pdata = + kmalloc(priv->dev_proc[seq].pfiles[j]. + num_items * sizeof(struct item_data), + GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles[j].pdata) { + PRINTM(ERROR, + "BT: Could not alloc memory for pdata!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata, + (u8 *)proc_files[j].pdata, + priv->dev_proc[seq].pfiles[j].num_items * + sizeof(struct item_data)); + for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items; + i++) { + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_DEV) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)&priv->bt_dev; + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_ADAPTER) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)priv->adapter; + } + priv->dev_proc[seq].pfiles[j].pbt = priv; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + entry = proc_create_data(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq].proc_entry, + proc_files[j].fops, + &priv->dev_proc[seq]. + pfiles[j]); + if (entry == NULL) +#else + entry = create_proc_entry(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq]. + proc_entry); + if (entry) { + entry->data = &priv->dev_proc[seq].pfiles[j]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + entry->owner = THIS_MODULE; +#endif + entry->proc_fops = proc_files[j].fops; + } else +#endif + PRINTM(MSG, "BT: Fail to create proc %s\n", + proc_files[j].name); + } + } +done: + if (ret == BT_STATUS_FAILURE) { + if (priv->dev_proc[seq].proc_entry) { + remove_proc_entry(m_dev->name, proc_mbt); + priv->dev_proc[seq].proc_entry = NULL; + } + if (priv->dev_proc[seq].pfiles) { + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + if (priv->dev_proc[seq].pfiles[j].pdata) { + kfree(priv->dev_proc[seq].pfiles[j]. + pdata); + priv->dev_proc[seq].pfiles[j].pdata = + NULL; + } + } + kfree(priv->dev_proc[seq].pfiles); + priv->dev_proc[seq].pfiles = NULL; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function removes proc interface + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_proc_remove(bt_private *priv) +{ + int j, i; + + ENTER(); + PRINTM(INFO, "BT: Remove Proc Interface\n"); + if (proc_mbt) { + for (i = 0; i < MAX_RADIO_FUNC; i++) { + if (!priv->dev_proc[i].proc_entry) + continue; + for (j = 0; j < ARRAY_SIZE(proc_files); j++) { + remove_proc_entry(proc_files[j].name, + priv->dev_proc[i].proc_entry); + } + remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt); + priv->dev_proc[i].proc_entry = NULL; + + if (priv->dev_proc[i].pfiles) { + for (j = 0; + j < priv->dev_proc[i].num_proc_files; + j++) { + if (priv->dev_proc[i].pfiles[j].pdata) { + kfree(priv->dev_proc[i]. + pfiles[j].pdata); + priv->dev_proc[i].pfiles[j]. + pdata = NULL; + } + } + kfree(priv->dev_proc[i].pfiles); + priv->dev_proc[i].pfiles = NULL; + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function creates proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_root_proc_init(void) +{ + PRINTM(INFO, "BT: Create Proc Interface\n"); + proc_mbt = proc_mkdir("mbt", PROC_DIR); + if (!proc_mbt) { + PRINTM(ERROR, "BT: Cannot create proc interface\n"); + return BT_STATUS_FAILURE; + } + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function removes proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS + */ +int +bt_root_proc_remove(void) +{ + remove_proc_entry("mbt", PROC_DIR); + proc_mbt = NULL; + return BT_STATUS_SUCCESS; +}
diff --git a/bt_sd8987/bt_char/bt_sdio.h b/bt_sd8987/bt_char/bt_sdio.h new file mode 100644 index 0000000..3fced8f --- /dev/null +++ b/bt_sd8987/bt_char/bt_sdio.h
@@ -0,0 +1,435 @@ +/** @file bt_sdio.h + * @brief This file contains SDIO (interface) module + * related macros, enum, and structure. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_SDIO_H_ +#define _BT_SDIO_H_ + +#include <linux/irqreturn.h> + +/** SD8787 card type */ +#define CARD_TYPE_SD8787 0x01 +/** SD8777 card type */ +#define CARD_TYPE_SD8777 0x02 +/** SD8887 card type */ +#define CARD_TYPE_SD8887 0x03 +/** SD8897 card type */ +#define CARD_TYPE_SD8897 0x04 +/** SD8797 card type */ +#define CARD_TYPE_SD8797 0x05 +/** SD8977 card type */ +#define CARD_TYPE_SD8977 0x06 +/** SD8997 card type */ +#define CARD_TYPE_SD8997 0x07 +/** SD8987 card type */ +#define CARD_TYPE_SD8987 0x08 +/** SD8978 card type */ +#define CARD_TYPE_SD8978 0x09 + +/** IRQ return type */ +typedef irqreturn_t IRQ_RET_TYPE; +/** IRQ return */ +#define IRQ_RET (return IRQ_HANDLED) +/** ISR notifier function */ +typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id, + struct pt_regs * reg); + +/** SDIO header length */ +#define SDIO_HEADER_LEN 4 + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */ +#define GPIO_INT_NEW_MODE 255 +/* SD block size can not bigger than 64 due to buf size limit in firmware */ +/** define SD block size for data Tx/Rx */ +#define SD_BLOCK_SIZE 64 +/** define SD block size for firmware download */ +#define SD_BLOCK_SIZE_FW_DL 256 + +/** Number of blocks for firmware transfer */ +#define FIRMWARE_TRANSFER_NBLOCK 2 + +/** Firmware ready */ +#define FIRMWARE_READY 0xfedc + +/* Bus Interface Control Reg 0x07 */ +/** SD BUS width 1 */ +#define SD_BUS_WIDTH_1 0x00 +/** SD BUS width 4 */ +#define SD_BUS_WIDTH_4 0x02 +/** SD BUS width mask */ +#define SD_BUS_WIDTH_MASK 0x03 +/** Asynchronous interrupt mode */ +#define ASYNC_INT_MODE 0x20 + +/** magic register */ +#define CARD_MAGIC_REG 0xF0 +/** magic value */ +#define MAGIC_VAL 0x24 + +/* Host Control Registers */ +/** Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/** Host Control Registers : Host without Command 53 finish host*/ +#define HOST_TO_CARD_EVENT (0x1U << 3) +/** Host Control Registers : Host terminates Command 53 */ +#define HOST_TERM_CMD53 (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Host interrupt RSR */ +#define HOST_INT_RSR_REG 0x01 + +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x02 + +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +#define HOST_INTSTATUS_REG 0x03 +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/ +#define ENABLE_GPIO_1_INT_MODE 0x88 +/** Scratch reg 3 2 : Configure GPIO-1 INT*/ +#define SCRATCH_REG_32 0xEE + +/** Host Control Registers : Host interrupt status */ +#define HOST_INT_STATUS_REG 0x28 +/** Host Control Registers : Upload CRC error */ +#define UP_LD_CRC_ERR (0x1U << 2) +/** Host Control Registers : Upload restart */ +#define UP_LD_RESTART (0x1U << 1) +/** Host Control Registers : Download restart */ +#define DN_LD_RESTART (0x1U << 0) + +/* Card Control Registers */ +/** Card Control Registers : Read SQ base address A0 register */ +#define SQ_READ_BASE_ADDRESS_A0_REG 0x40 +/** Card Control Registers : Read SQ base address A1 register */ +#define SQ_READ_BASE_ADDRESS_A1_REG 0x41 +/** Card Control Registers : Read SQ base address A2 register */ +#define SQ_READ_BASE_ADDRESS_A2_REG 0x42 +/** Card Control Registers : Read SQ base address A3 register */ +#define SQ_READ_BASE_ADDRESS_A3_REG 0x43 +/** Card Control Registers : Read SQ base address B0 register */ +#define SQ_READ_BASE_ADDRESS_B0_REG 0x44 +/** Card Control Registers : Read SQ base address B1 register */ +#define SQ_READ_BASE_ADDRESS_B1_REG 0x45 +/** Card Control Registers : Read SQ base address B2 register */ +#define SQ_READ_BASE_ADDRESS_B2_REG 0x46 +/** Card Control Registers : Read SQ base address B3 register */ +#define SQ_READ_BASE_ADDRESS_B3_REG 0x47 + +/** Card Control Registers : Card status register */ +#define CARD_STATUS_REG 0x30 +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host interrupt mask register */ +#define HOST_INTERRUPT_MASK_REG 0x34 +/** Card Control Registers : Host power interrupt mask */ +#define HOST_POWER_INT_MASK (0x1U << 3) +/** Card Control Registers : Abort card interrupt mask */ +#define ABORT_CARD_INT_MASK (0x1U << 2) +/** Card Control Registers : Upload card interrupt mask */ +#define UP_LD_CARD_INT_MASK (0x1U << 1) +/** Card Control Registers : Download card interrupt mask */ +#define DN_LD_CARD_INT_MASK (0x1U << 0) + +/** Card Control Registers : Card interrupt status register */ +#define CARD_INTERRUPT_STATUS_REG 0x38 +/** Card Control Registers : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** Card Control Registers : Card interrupt RSR register */ +#define CARD_INTERRUPT_RSR_REG 0x3c +/** Card Control Registers : Power up RSR */ +#define POWER_UP_RSR (0x1U << 4) +/** Card Control Registers : Power down RSR */ +#define POWER_DOWN_RSR (0x1U << 3) + +/** Card Control Registers : Debug 0 register */ +#define DEBUG_0_REG 0x70 +/** Card Control Registers : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : Debug 1 register */ +#define DEBUG_1_REG 0x71 +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : Debug 2 register */ +#define DEBUG_2_REG 0x72 +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : Debug 3 register */ +#define DEBUG_3_REG 0x73 +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0x78 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0x79 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0x7A + +/** Firmware status 0 register */ +#define CARD_FW_STATUS0_REG 0x60 +/** Firmware status 1 register */ +#define CARD_FW_STATUS1_REG 0x61 +/** Rx length register */ +#define CARD_RX_LEN_REG 0x62 +/** Rx unit register */ +#define CARD_RX_UNIT_REG 0x63 +/** Card Control Registers : Miscellaneous Configuration Register */ +#define CARD_MISC_CFG_REG 0x6C +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT (0x1U << 4) + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0x68 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0x69 +/** Card Control Registers : Card OCR 3 register */ +#define CARD_OCR_3_REG 0x6A +/** Card Control Registers : Card config register */ +#define CARD_CONFIG_REG 0x6B +/** Card Control Registers : Card revision register */ +#define CARD_REVISION_REG 0x5c +/** Card Control Registers : Command 53 finish G BUS */ +#define CMD53_FINISH_GBUS (0x1U << 1) +/** Card Control Registers : SD negative edge */ +#define SD_NEG_EDGE (0x1U << 0) + +/* Special registers in function 0 of the SDxx card */ +/** Special register in function 0 of the SDxxx card : Scratch 0 */ +#define SCRATCH_0_REG 0x80fe +/** Special register in function 0 of the SDxxx card : Scratch 1 */ +#define SCRATCH_1_REG 0x80ff +/** Host F1 read base 0 */ +#define HOST_F1_RD_BASE_0 0x0040 +/** Host F1 read base 1 */ +#define HOST_F1_RD_BASE_1 0x0041 +/** Host F1 card ready */ +#define HOST_F1_CARD_RDY 0x0020 + +/** Chip Id Register 0 */ +#define CARD_CHIP_ID_0_REG 0x801c +/** Chip Id Register 1 */ +#define CARD_CHIP_ID_1_REG 0x801d + +struct sdio_mmc_card { + /** sdio_func structure pointer */ + struct sdio_func *func; + /** bt_private structure pointer */ + bt_private *priv; +}; + +struct sdio_card_reg { + u8 cfg; + u8 host_int_mask; // HOST_INT_MASK_REG + u8 host_intstatus; // HOST_INTSTATUS_REG + u8 host_int_rsr_reg; // HOST_INT_RSR_REG + u8 card_misc_cfg_reg; // CARD_MISC_CFG_REG + u8 card_status; // CARD_STATUS_REG + u8 sq_read_base_addr_a0; // SQ_READ_BASE_ADDRESS_A0_REG + u8 sq_read_base_addr_a1; // SQ_READ_BASE_ADDRESS_A1_REG + u8 card_revision; // CARD_REVISION_REG + u8 card_fw_status0; // CARD_FW_STATUS0_REG + u8 card_fw_status1; // CARD_FW_STATUS1_REG + u8 card_rx_len; // CARD_RX_LEN_REG + u8 card_rx_unit; // CARD_RX_UNIT_REG + u8 io_port_0; // IO_PORT_0_REG + u8 io_port_1; // IO_PORT_1_REG + u8 io_port_2; // IO_PORT_2_REG +}; + +struct sdio_device { + const struct sdio_card_reg *reg; +}; + +static const struct sdio_card_reg bt_reg_87xx = { + .cfg = 0x00, + .host_int_mask = 0x02, // HOST_INT_MASK_REG + .host_intstatus = 0x03, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x01, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0x6c, // CARD_MISC_CFG_REG + .card_status = 0x30, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x40, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x41, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0x5C, // CARD_REVISION_REG + .card_fw_status0 = 0x60, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0x61, // CARD_FW_STATUS1_REG + .card_rx_len = 0x62, // CARD_RX_LEN_REG + .card_rx_unit = 0x63, // CARD_RX_UNIT_REG + .io_port_0 = 0x78, // IO_PORT_0_REG + .io_port_1 = 0x79, // IO_PORT_1_REG + .io_port_2 = 0x7a, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8887 = { + .cfg = 0x00, + .host_int_mask = 0x08, // HOST_INT_MASK_REG + .host_intstatus = 0x0C, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x04, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xD8, // CARD_MISC_CFG_REG + .card_status = 0x5C, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x6C, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x6D, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xC8, // CARD_REVISION_REG + .card_fw_status0 = 0x88, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0x89, // CARD_FW_STATUS1_REG + .card_rx_len = 0x8A, // CARD_RX_LEN_REG + .card_rx_unit = 0x8B, // CARD_RX_UNIT_REG + .io_port_0 = 0xE4, // IO_PORT_0_REG + .io_port_1 = 0xE5, // IO_PORT_1_REG + .io_port_2 = 0xE6, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8897 = { + .cfg = 0x00, + .host_int_mask = 0x02, // HOST_INT_MASK_REG + .host_intstatus = 0x03, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x01, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xCC, // CARD_MISC_CFG_REG + .card_status = 0x50, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0x60, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0x61, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xBC, // CARD_REVISION_REG + .card_fw_status0 = 0xC0, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0xC1, // CARD_FW_STATUS1_REG + .card_rx_len = 0xC2, // CARD_RX_LEN_REG + .card_rx_unit = 0xC3, // CARD_RX_UNIT_REG + .io_port_0 = 0xD8, // IO_PORT_0_REG + .io_port_1 = 0xD9, // IO_PORT_1_REG + .io_port_2 = 0xDA, // IO_PORT_2_REG +}; + +static const struct sdio_card_reg bt_reg_8977_8978_8997 = { + .cfg = 0x00, + .host_int_mask = 0x08, // HOST_INT_MASK_REG + .host_intstatus = 0x0C, // HOST_INTSTATUS_REG + .host_int_rsr_reg = 0x04, // HOST_INT_RSR_REG + .card_misc_cfg_reg = 0xD8, // CARD_MISC_CFG_REG + .card_status = 0x5C, // CARD_STATUS_REG + .sq_read_base_addr_a0 = 0xF8, // SQ_READ_BASE_ADDRESS_A0_REG + .sq_read_base_addr_a1 = 0xF9, // SQ_READ_BASE_ADDRESS_A1_REG + .card_revision = 0xC8, // CARD_REVISION_REG + .card_fw_status0 = 0xE8, // CARD_FW_STATUS0_REG + .card_fw_status1 = 0xE9, // CARD_FW_STATUS1_REG + .card_rx_len = 0xEA, // CARD_RX_LEN_REG + .card_rx_unit = 0xEB, // CARD_RX_UNIT_REG + .io_port_0 = 0xE4, // IO_PORT_0_REG + .io_port_1 = 0xE5, // IO_PORT_1_REG + .io_port_2 = 0xE6, // IO_PORT_2_REG +}; + +static const struct sdio_device bt_sdio_sd8787 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8777 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8887 = { + .reg = &bt_reg_8887, +}; + +static const struct sdio_device bt_sdio_sd8897 = { + .reg = &bt_reg_8897, +}; + +static const struct sdio_device bt_sdio_sd8797 = { + .reg = &bt_reg_87xx, +}; + +static const struct sdio_device bt_sdio_sd8977 = { + .reg = &bt_reg_8977_8978_8997, +}; + +static const struct sdio_device bt_sdio_sd8978 = { + .reg = &bt_reg_8977_8978_8997, +}; + +static const struct sdio_device bt_sdio_sd8997 = { + .reg = &bt_reg_8977_8978_8997, +}; + +static const struct sdio_device bt_sdio_sd8987 = { + .reg = &bt_reg_8977_8978_8997, +}; + +/** DMA alignment value */ +#define DMA_ALIGNMENT 64 +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** This function read cmd52 register */ +int sd_write_reg(bt_private *priv, int reg, u8 val); +/** This function write cmd52 value to register */ +int sd_read_reg(bt_private *priv, int reg, u8 *data); +/** This function reads the Cmd52 value in dev structure */ +int sd_read_cmd52_val(bt_private *priv); +/** This function updates card reg based on the Cmd52 value in dev structure */ +int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val); + +void sdio_update_card_type(bt_private *priv, void *card); +int sdio_get_sdio_device(bt_private *priv); + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** This function tells lower driver that BT is suspended */ +void bt_is_suspended(bt_private *priv); +#endif +#endif +#endif +#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8987/bt_char/bt_sdiommc.c b/bt_sd8987/bt_char/bt_sdiommc.c new file mode 100644 index 0000000..d7f51bb --- /dev/null +++ b/bt_sd8987/bt_char/bt_sdiommc.c
@@ -0,0 +1,2397 @@ +/** @file bt_sdiommc.c + * @brief This file contains SDIO IF (interface) module + * related functions. + * + * Copyright (C) 2007-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/firmware.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/card.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** define marvell vendor id */ +#define MARVELL_VENDOR_ID 0x02df + +/** Max retry number of CMD53 read/write */ +#define MAX_CMD53_RETRY 3 +/** Max retry number of CMD53 read/write */ +#define MAX_CMD52_RETRY 3 +/** Firmware name */ +static char *fw_name; +/** fw serial download flag */ +extern int bt_fw_serial; +int bt_intmode = INT_MODE_SDIO; +/** request firmware nowait */ +int bt_req_fw_nowait; +static int multi_fn = BIT(2); + +#define DEFAULT_FW_NAME "" + +/** FW header length for CRC check disable */ +#define FW_CRC_HEADER_RB2 28 +/** FW header for CRC check disable */ +static u8 fw_crc_header_rb_2[FW_CRC_HEADER_RB2] = { + 0x05, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x9d, 0x32, 0xbb, 0x11, 0x01, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x67, 0xd6, 0xfc, 0x25 +}; + +/** FW header length for CRC check disable */ +#define FW_CRC_HEADER_RB 24 +/** FW header for CRC check disable */ +static u8 fw_crc_header_rb_1[FW_CRC_HEADER_RB] = { + 0x01, 0x00, 0x00, 0x00, 0x04, 0xfd, 0x00, 0x04, + 0x08, 0x00, 0x00, 0x00, 0x26, 0x52, 0x2a, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** Default firmware name */ +#define DEFAULT_FW_NAME_8777 "mrvl/sd8777_uapsta.bin" +#define DEFAULT_FW_NAME_8787 "mrvl/sd8787_uapsta.bin" +#define DEFAULT_FW_NAME_8797 "mrvl/sd8797_uapsta.bin" +#define DEFAULT_FW_NAME_8887 "mrvl/sd8887_uapsta.bin" +#define DEFAULT_FW_NAME_8897 "mrvl/sd8897_uapsta.bin" +#define DEFAULT_FW_NAME_8977 "mrvl/sdsd8977_combo.bin" +#define DEFAULT_FW_NAME_8978 "mrvl/sdsd8978_combo.bin" +#define DEFAULT_FW_NAME_8997 "mrvl/sdsd8997_combo.bin" + +/** SD8787 chip revision ID */ +#define SD8787_W0 0x30 +#define SD8787_W1 0x31 +#define SD8787_A0_A1 0x40 +/** SD8797 chip revision ID */ +#define SD8797_A0 0x00 +#define SD8797_B0 0x10 +/** SD8897 chip revision ID */ +#define SD8897_A0 0x10 +#define SD8897_B0 0x20 + +/** SD8887 chip revision ID */ +#define SD8887_A0 0x0 +#define SD8887_A2 0x2 +#define SD8887_A0_FW_NAME "mrvl/sd8887_uapsta.bin" +#define SD8887_A2_FW_NAME "mrvl/sd8887_uapsta_a2.bin" +#define SD8887_A2_BT_FW_NAME "mrvl/sd8887_bt_a2.bin" + +#define SD8897_A0_FW_NAME "mrvl/sd8897_uapsta_a0.bin" +#define SD8897_B0_FW_NAME "mrvl/sd8897_uapsta.bin" + +#define SD8787_W1_FW_NAME "mrvl/sd8787_uapsta_w1.bin" +#define SD8787_AX_FW_NAME "mrvl/sd8787_uapsta.bin" +#define SD8797_A0_FW_NAME "mrvl/sd8797_uapsta_a0.bin" +#define SD8797_B0_FW_NAME "mrvl/sd8797_uapsta.bin" + +/** SD8977 chip revision ID */ +#define SD8977_V0 0x0 +#define SD8977_V1 0x8 +#define SD8977_V2 0x9 +#define SD8977_V0_FW_NAME "mrvl/sdsd8977_combo.bin" +#define SD8977_V0_BT_FW_NAME "mrvl/sd8977_bt.bin" +#define SD8977_V1_FW_NAME "mrvl/sdsd8977_combo_v1.bin" +#define SD8977_V1_BT_FW_NAME "mrvl/sd8977_bt_v1.bin" +#define SD8977_V2_FW_NAME "mrvl/sdsd8977_combo_v2.bin" +#define SD8977_V2_BT_FW_NAME "mrvl/sd8977_bt_v2.bin" +/** SD8978 FW NAME */ +#define SD8978_FW_NAME "mrvl/sdsd8978_combo.bin" +#define SD8978_BT_FW_NAME "mrvl/sd8978_bt.bin" + +/** SD8997 chip revision ID */ +#define SD8997_Z 0x02 +#define SD8997_V2 0x10 +#define SD8997_Z_FW_NAME "mrvl/sdsd8997_combo.bin" +#define SD8997_Z_BT_FW_NAME "mrvl/sd8997_bt.bin" +#define SD8997_V2_FW_NAME "mrvl/sdsd8997_combo_v2.bin" +#define SD8997_V2_BT_FW_NAME "mrvl/sd8997_bt_v2.bin" +#define SD8997_V3_FW_NAME "mrvl/sdsd8997_combo_v3.bin" +#define SD8997_V3_BT_FW_NAME "mrvl/sd8997_bt_v3.bin" + +/** SD8987 */ +#define SD8987_FW_NAME "mrvl/sdsd8987_combo.bin" +#define SD8987_BT_FW_NAME "mrvl/sd8987_bt.bin" + +/** Function number 2 */ +#define FN2 2 +/** Device ID for SD8787 FN2 */ +#define SD_DEVICE_ID_8787_BT_FN2 0x911A +/** Device ID for SD8787 FN3 */ +#define SD_DEVICE_ID_8787_BT_FN3 0x911B +/** Device ID for SD8777 FN2 */ +#define SD_DEVICE_ID_8777_BT_FN2 0x9132 +/** Device ID for SD8777 FN3 */ +#define SD_DEVICE_ID_8777_BT_FN3 0x9133 +/** Device ID for SD8887 FN2 */ +#define SD_DEVICE_ID_8887_BT_FN2 0x9136 +/** Device ID for SD8887 FN3 */ +#define SD_DEVICE_ID_8887_BT_FN3 0x9137 +/** Device ID for SD8897 FN2 */ +#define SD_DEVICE_ID_8897_BT_FN2 0x912E +/** Device ID for SD8897 FN3 */ +#define SD_DEVICE_ID_8897_BT_FN3 0x912F +/** Device ID for SD8797 FN2 */ +#define SD_DEVICE_ID_8797_BT_FN2 0x912A +/** Device ID for SD8797 FN3 */ +#define SD_DEVICE_ID_8797_BT_FN3 0x912B +/** Device ID for SD8977 FN2 */ +#define SD_DEVICE_ID_8977_BT_FN2 0x9146 +/** Device ID for SD8978 FN2 */ +#define SD_DEVICE_ID_8978_BT_FN2 0x915a +/** Device ID for SD8997 FN2 */ +#define SD_DEVICE_ID_8997_BT_FN2 0x9142 +/** Device ID for SD8987 FN2 */ +#define SD_DEVICE_ID_8987_BT_FN2 0x914a +/** Device ID for SD8987 FN3 */ +#define SD_DEVICE_ID_8987_BT_FN3 0x914b + +/** Array of SDIO device ids when multi_fn=0x12 */ +static const struct sdio_device_id bt_ids[] = { + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8978_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)}, + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8987_BT_FN2)}, + {} +}; + +MODULE_DEVICE_TABLE(sdio, bt_ids); + +#ifdef SDIO_OOB_IRQ +extern int mrvl_sdio_claim_irq(struct sdio_func *func, + sdio_irq_handler_t * handler); +extern int mrvl_sdio_release_irq(struct sdio_func *func); +extern int mrvl_sdio_suspend(struct sdio_func *func); +extern int mrvl_sdio_resume(struct sdio_func *func); +#endif + +/******************************************************** + Global Variables +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +extern int mbt_pm_keep_power; +#endif + +extern bt_private *m_priv[]; +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function gets rx_unit value + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_get_rx_unit(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_unit_reg = priv->psdio_device->reg->card_rx_unit; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_unit_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + priv->bt_dev.rx_unit = reg; + + LEAVE(); + return ret; +} + +/** + * @brief This function reads fwstatus registers + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_read_firmware_status(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 fws0; + u8 fws1; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0; + u8 card_fw_status1_reg = priv->psdio_device->reg->card_fw_status1; + + ENTER(); + + fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + *dat = (((u16) fws1) << 8) | fws0; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function reads rx length + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sd_read_rx_len(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_len_reg = priv->psdio_device->reg->card_rx_len; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_len_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + *dat = (u16) reg << priv->bt_dev.rx_unit; + + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts mask + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_enable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask; + + ENTER(); + + sdio_writeb(card->func, mask, host_int_mask_reg, &ret); + if (ret) { + PRINTM(WARN, "BT: Unable to enable the host interrupt!\n"); + ret = BT_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** @brief This function disables the host interrupts mask. + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sbi_disable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_FAILURE; + u8 host_int_mask; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = priv->psdio_device->reg->host_int_mask; + + ENTER(); + + /* Read back the host_int_mask register */ + host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret); + if (ret) + goto done; + + /* Update with the mask and write back to the register */ + host_int_mask &= ~mask; + sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret); + if (ret < 0) { + PRINTM(WARN, "BT: Unable to diable the host interrupt!\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function polls the card status register + * + * @param priv A pointer to bt_private structure + * @param bits the bit mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_poll_card_status(bt_private *priv, u8 bits) +{ + int tries; + int rval; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 cs; + u8 card_status_reg = priv->psdio_device->reg->card_status; + + ENTER(); + + for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { + cs = sdio_readb(card->func, card_status_reg, &rval); + if (rval != 0) + break; + if (rval == 0 && (cs & bits) == bits) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + udelay(1); + } + PRINTM(ERROR, + "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n", + rval, tries, cs); + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_cmd52_val(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 func, reg, val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + func = priv->bt_dev.cmd52_func; + reg = priv->bt_dev.cmd52_reg; + sdio_claim_host(card->func); + if (func) + val = sdio_readb(card->func, reg, &ret); + else + val = sdio_f0_readb(card->func, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n", + func, reg); + } else { + priv->bt_dev.cmd52_val = val; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param func Stores func variable + * @param reg Stores reg variable + * @param val Stores val variable + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_cmd52_val(bt_private *priv, int func, int reg, int val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + if (val >= 0) { + /* Perform actual write only if val is provided */ + sdio_claim_host(card->func); + if (func) + sdio_writeb(card->func, val, reg, &ret); + else + sdio_f0_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, + "BT: Cannot write value (0x%x) to func %d reg %d\n", + val, func, reg); + goto done; + } + priv->bt_dev.cmd52_val = val; + } + + /* Save current func and reg for future read */ + priv->bt_dev.cmd52_func = func; + priv->bt_dev.cmd52_reg = reg; + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to write + * @param val value + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_reg(bt_private *priv, int reg, u8 val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + sdio_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to read + * @param data Data + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_reg(bt_private *priv, int reg, u8 *data) +{ + int ret = BT_STATUS_SUCCESS; + u8 val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + *data = val; + LEAVE(); + return ret; +} + +/** + * @brief This function reads FN0 reg value + * + * @param priv A pointer to bt_private structure + * @param reg register to read + * @param data Data + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_f0_read_reg(bt_private *priv, int reg, u8 *data) +{ + int ret = BT_STATUS_SUCCESS; + u8 val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + val = sdio_f0_readb(card->func, reg, &ret); + sdio_release_host(card->func); + *data = val; + LEAVE(); + return ret; +} + +/** + * @brief This function probes the card + * + * @param func A pointer to sdio_func structure. + * @param id A pointer to structure sdio_device_id + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = NULL; + struct sdio_mmc_card *card = NULL; + + ENTER(); + + PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor, + id->device, id->class, func->num); + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto done; + } + card->func = func; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + /* wait for chip fully wake up */ + if (!func->enable_timeout) + func->enable_timeout = 200; +#endif + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) { + sdio_disable_func(func); + sdio_release_host(func); + PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret); + kfree(card); + LEAVE(); + return -EIO; + } + sdio_release_host(func); + priv = bt_add_card(card); + if (!priv) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + ret = BT_STATUS_FAILURE; + kfree(card); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks if the firmware is ready to accept + * command or not. + * + * @param priv A pointer to bt_private structure + * @param pollnum Number of times to poll fw status + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_verify_fw_download(bt_private *priv, int pollnum) +{ + int ret = BT_STATUS_FAILURE; + u16 firmwarestat = 0; + int tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if (sd_read_firmware_status(priv, &firmwarestat) < 0) + continue; + if (firmwarestat == FIRMWARE_READY) { + PRINTM(MSG, "BT FW is active(%d)\n", tries); + ret = BT_STATUS_SUCCESS; + break; + } + mdelay(100); + } + if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) { + PRINTM(ERROR, + "Fail to poll firmware status: firmwarestat=0x%x\n", + firmwarestat); + bt_dump_sdio_regs(priv); + } + LEAVE(); + return ret; +} + +/** + * @brief Transfers firmware to card + * + * @param priv A Pointer to bt_private structure + * @param fw A Pointer to fw image + * @param fw_len fw image len + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len) +{ + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 *firmware = fw; + int firmwarelen = fw_len; + u8 base0; + u8 base1; + int ret = BT_STATUS_SUCCESS; + int offset; + void *tmpfwbuf = NULL; + int tmpfwbufsz; + u8 *fwbuf; + u16 len; + int txlen = 0; + int tx_blocks = 0; + int i = 0; + int tries = 0; +#ifdef FW_DOWNLOAD_SPEED + u32 tv1, tv2; +#endif + u8 sq_read_base_address_a0_reg = + priv->psdio_device->reg->sq_read_base_addr_a0; + u8 sq_read_base_address_a1_reg = + priv->psdio_device->reg->sq_read_base_addr_a1; + u8 crc_buffer = 0; + u8 *header_crc_fw = NULL; + u8 header_crc_fw_len = 0; + + if (priv->card_type == CARD_TYPE_SD8787) { + header_crc_fw = fw_crc_header_rb_1; + header_crc_fw_len = FW_CRC_HEADER_RB; + } else if (priv->card_type == CARD_TYPE_SD8777) { + header_crc_fw = fw_crc_header_rb_2; + header_crc_fw_len = FW_CRC_HEADER_RB2; + } + + ENTER(); + + PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen); + +#ifdef FW_DOWNLOAD_SPEED + tv1 = get_utimeofday(); +#endif + + tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT; + tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); + if (!tmpfwbuf) { + PRINTM(ERROR, + "BT: Unable to allocate buffer for firmware. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + /* Ensure aligned firmware buffer */ + fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); + + if (!(priv->fw_crc_check) + && ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + ) { + /* CRC check not required, use custom header first */ + firmware = header_crc_fw; + firmwarelen = header_crc_fw_len; + crc_buffer = 1; + } + + /* Perform firmware data transfer */ + offset = 0; + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits + */ + ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret < 0) { + PRINTM(FATAL, + "BT: FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + if (!crc_buffer) + /* More data? */ + if (offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + base0 = sdio_readb(card->func, + sq_read_base_address_a0_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", + base0, base0); + ret = BT_STATUS_FAILURE; + goto done; + } + base1 = sdio_readb(card->func, + sq_read_base_address_a1_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download\n", + base1, base1); + ret = BT_STATUS_FAILURE; + goto done; + } + len = (((u16) base1) << 8) | base0; + + if (len != 0) + break; + udelay(10); + } + + if (len == 0) + break; + else if (len > BT_UPLD_SIZE) { + PRINTM(FATAL, + "BT: FW download failure @ %d, invalid length %d\n", + offset, len); + ret = BT_STATUS_FAILURE; + goto done; + } + /** ignore CRC check before download the first packet */ + if (offset == 0 && (len & BIT(0))) + len &= ~BIT(0); + txlen = len; + + if (len & BIT(0)) { + i++; + if (i >= MAX_CMD53_RETRY) { + PRINTM(FATAL, + "BT: FW download failure @ %d, over max retry count\n", + offset); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: FW CRC error indicated by the helper:" + " len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + + PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last block */ + if (firmwarelen - offset < txlen) + txlen = firmwarelen - offset; + + PRINTM(INFO, "."); + + tx_blocks = + (txlen + SD_BLOCK_SIZE_FW_DL - + 1) / SD_BLOCK_SIZE_FW_DL; + + /* Copy payload to buffer */ + memcpy(fwbuf, &firmware[offset], txlen); + } + + /* Send data */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf, + tx_blocks * SD_BLOCK_SIZE_FW_DL); + + if (ret < 0) { + PRINTM(ERROR, + "BT: FW download, write iomem (%d) failed @ %d\n", + i, offset); + sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret); + if (ret) + PRINTM(ERROR, "write ioreg failed (CFG)\n"); + } + + offset += txlen; + if (crc_buffer + && ((priv->card_type == CARD_TYPE_SD8787) || + (priv->card_type == CARD_TYPE_SD8777)) + ) { + if (offset >= header_crc_fw_len) { + /* Custom header download complete, restore + original FW */ + offset = 0; + firmware = fw; + firmwarelen = fw_len; + crc_buffer = 0; + } + } + } while (TRUE); + + PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset); + + ret = BT_STATUS_SUCCESS; +done: +#ifdef FW_DOWNLOAD_SPEED + tv2 = get_utimeofday(); + PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000, + (tv1 % 1000000) / 1000, tv1 % 1000); + PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); + tv2 -= tv1; + PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); +#endif + kfree(tmpfwbuf); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * + * @param fw_firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_request_fw_dpc(const struct firmware *fw_firmware, void *context) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = (bt_private *)context; + struct sdio_mmc_card *card = NULL; + struct m_dev *m_dev_bt = NULL; + struct timeval tstamp; + int index; + + ENTER(); + + m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ]; + + if ((priv == NULL) || (priv->adapter == NULL) || + (priv->bt_dev.card == NULL) || (m_dev_bt == NULL) + ) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + card = (struct sdio_mmc_card *)priv->bt_dev.card; + + if (!fw_firmware) { + do_gettimeofday(&tstamp); + if (tstamp.tv_sec > + (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(ERROR, + "BT: No firmware image found. Skipping download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: No firmware image found! Retrying download\n"); + /* Wait a second here before calling the callback again */ + os_sched_timeout(1000); + sd_download_firmware_w_helper(priv); + LEAVE(); + return ret; + } + + priv->firmware = fw_firmware; + + if (BT_STATUS_FAILURE == + sd_init_fw_dpc(priv, (u8 *)priv->firmware->data, + priv->firmware->size)) { + PRINTM(ERROR, + "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n", + bt_req_fw_nowait); + sdio_release_host(card->func); + ret = BT_STATUS_FAILURE; + goto done; + } + + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) { + PRINTM(ERROR, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + sdio_release_host(card->func); + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + LEAVE(); + return ret; + +done: + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + /* For synchronous download cleanup will be done in add_card */ + if (!bt_req_fw_nowait) + return ret; + PRINTM(INFO, "unregister device\n"); + sbi_unregister_dev(priv); + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); + bt_proc_remove(priv); + clean_up_m_devs(priv); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return None + **/ +static void +sd_request_fw_callback(const struct firmware *firmware, void *context) +{ + ENTER(); + sd_request_fw_dpc(firmware, context); + LEAVE(); + return; +} + +/** + * @brief This function downloads firmware image to the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sd_download_firmware_w_helper(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + int err; + char *cur_fw_name = NULL; + + ENTER(); + + cur_fw_name = fw_name; + + if (fw_name == NULL) { + if (priv->card_type == CARD_TYPE_SD8787) + cur_fw_name = DEFAULT_FW_NAME_8787; + else if (priv->card_type == CARD_TYPE_SD8777) + cur_fw_name = DEFAULT_FW_NAME_8777; + else if (priv->card_type == CARD_TYPE_SD8887) { + /* Check revision ID */ + switch (priv->adapter->chip_rev) { + case SD8887_A0: + cur_fw_name = SD8887_A0_FW_NAME; + break; + case SD8887_A2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8887_A2_FW_NAME; + else + cur_fw_name = SD8887_A2_BT_FW_NAME; + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8887; + break; + } + } else if (priv->card_type == CARD_TYPE_SD8897) + cur_fw_name = DEFAULT_FW_NAME_8897; + else if (priv->card_type == CARD_TYPE_SD8797) + cur_fw_name = DEFAULT_FW_NAME_8797; + else if (priv->card_type == CARD_TYPE_SD8977) { + switch (priv->adapter->chip_rev) { + case SD8977_V0: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V0_FW_NAME; + else + cur_fw_name = SD8977_V0_BT_FW_NAME; + break; + case SD8977_V1: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V1_FW_NAME; + else + cur_fw_name = SD8977_V1_BT_FW_NAME; + break; + case SD8977_V2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8977_V2_FW_NAME; + else + cur_fw_name = SD8977_V2_BT_FW_NAME; + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8977; + break; + } + } else if (priv->card_type == CARD_TYPE_SD8978) { + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8978_FW_NAME; + else + cur_fw_name = SD8978_BT_FW_NAME; + } else if (priv->card_type == CARD_TYPE_SD8997) + switch (priv->adapter->chip_rev) { + case SD8997_Z: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8997_Z_FW_NAME; + else + cur_fw_name = SD8997_Z_BT_FW_NAME; + break; + case SD8997_V2: + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) { + if (priv->adapter->magic_val == + MAGIC_VAL) + cur_fw_name = SD8997_V3_FW_NAME; + else + cur_fw_name = SD8997_V2_FW_NAME; + } else { + if (priv->adapter->magic_val == + MAGIC_VAL) + cur_fw_name = + SD8997_V3_BT_FW_NAME; + else + cur_fw_name = + SD8997_V2_BT_FW_NAME; + } + break; + default: + cur_fw_name = DEFAULT_FW_NAME_8997; + break; + } else if (priv->card_type == CARD_TYPE_SD8987) { + if (bt_fw_serial == 1 + && !priv->fw_reload && !bt_fw_reload) + cur_fw_name = SD8987_FW_NAME; + else + cur_fw_name = SD8987_BT_FW_NAME; + } + } + + PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + GFP_KERNEL, priv, + sd_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#endif +#endif + if (ret < 0) + PRINTM(FATAL, + "BT: request_firmware_nowait() failed, error code = %#x\n", + ret); + } else { + err = request_firmware(&priv->firmware, cur_fw_name, + priv->hotplug_device); + if (err < 0) { + PRINTM(FATAL, + "BT: request_firmware() failed, error code = %#x\n", + err); + ret = BT_STATUS_FAILURE; + } else + ret = sd_request_fw_dpc(priv->firmware, priv); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function reads data from the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_card_to_host(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 buf_len = 0; + int buf_block_len; + int blksz; + struct sk_buff *skb = NULL; + u32 type; + u8 *payload = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + struct m_dev *mdev_debug = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + struct debug_dev *debug_dev = + (struct debug_dev *)priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer; + struct sdio_mmc_card *card = priv->bt_dev.card; + int i = 0; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + do { + /* Read the length of data to be transferred */ + ret = sd_read_rx_len(priv, &buf_len); + if (ret < 0) { + i++; + PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i); + if (i >= MAX_CMD52_RETRY) { + ret = BT_STATUS_FAILURE; + goto exit; + } + udelay(20); + } + } + while (ret == BT_STATUS_FAILURE); + + /* Allocate buffer */ + blksz = SD_BLOCK_SIZE; + buf_block_len = (buf_len + blksz - 1) / blksz; + if (buf_len <= BT_HEADER_LEN || + (buf_block_len * blksz) > ALLOC_BUF_SIZE) { + PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n", + buf_len); + ret = BT_STATUS_FAILURE; + goto exit; + } + skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + goto exit; + } + if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) { + skb_put(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + skb_pull(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + } + + payload = skb->data; + i = 0; + do { + ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: card_to_host, read iomem (%d) failed: %d\n", + i, ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) { + kfree_skb(skb); + skb = NULL; + goto exit; + } + } + } while (ret == BT_STATUS_FAILURE); + /* This is SDIO specific header length: byte[2][1][0], * type: byte[3] + (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ + buf_len = payload[0]; + buf_len |= (u16) payload[1] << 8; + type = payload[3]; + PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", mbt_dev->name, + buf_len, type); + if (buf_len > buf_block_len * blksz) { + PRINTM(ERROR, + "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n", + buf_len, buf_block_len * blksz); + ret = BT_STATUS_FAILURE; + kfree_skb(skb); + skb = NULL; + goto exit; + } + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len); + switch (type) { + case HCI_ACLDATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (*(u16 *) skb->data == 0xffff) { + bt_store_firmware_dump(priv, skb->data, skb->len); + dev_kfree_skb_any(skb); + break; + } + bt_recv_frame(priv, skb); + break; + case HCI_SCODATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + bt_recv_frame(priv, skb); + break; + case HCI_EVENT_PKT: + /** add EVT Demux */ + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb)) + break; + switch (skb->data[0]) { + case 0x0E: + /** cmd complete */ + if (priv->debug_device_pending) { + if (priv->debug_ocf_ogf[0] == skb->data[3] && + priv->debug_ocf_ogf[1] == skb->data[4]) { + priv->debug_device_pending = 0; + priv->debug_ocf_ogf[0] = 0; + priv->debug_ocf_ogf[1] = 0; + /** debug cmd complete */ + if (debug_dev) { + skb->dev = (void *)mdev_debug; + mdev_recv_frame(skb); + mdev_debug->stat.byte_rx += + buf_len; + } + break; + } + } + bt_recv_frame(priv, skb); + break; + case 0x0F: + /** cmd status */ + bt_recv_frame(priv, skb); + break; + case 0xFF: + /** Vendor specific pkt */ + bt_recv_frame(priv, skb); + break; + default: + bt_recv_frame(priv, skb); + break; + } + break; + case MRVL_VENDOR_PKT: + /* Just think here need to back compatible FM */ + bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) + bt_recv_frame(priv, skb); + break; + default: + /* Driver specified event and command resp should be handle + here */ + PRINTM(INFO, "BT: Unknown PKT type:%d\n", type); + kfree_skb(skb); + skb = NULL; + break; + } +exit: + if (ret) { + if (mbt_dev) + mdev_bt->stat.err_rx++; + PRINTM(ERROR, "error when recv pkt!\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function removes the card + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_remove_card(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + card = sdio_get_drvdata(func); + if (card) { + bt_remove_card(card->priv); + kfree(card); + } + } + + LEAVE(); +} + +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_interrupt(struct sdio_func *func) +{ + bt_private *priv; + struct m_dev *m_dev = NULL; + struct sdio_mmc_card *card; + int ret = BT_STATUS_SUCCESS; + u8 ireg = 0; + u8 host_intstatus_reg = 0; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->priv) { + PRINTM(INFO, + "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", + __func__, func, card); + LEAVE(); + return; + } + priv = card->priv; + host_intstatus_reg = priv->psdio_device->reg->host_intstatus; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + if (priv->card_type == CARD_TYPE_SD8887 || + priv->card_type == CARD_TYPE_SD8897 || + priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987 || + priv->card_type == CARD_TYPE_SD8978) { + ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0, + SD_BLOCK_SIZE); + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n", + ret); + goto done; + } + ireg = priv->adapter->hw_regs[host_intstatus_reg]; + } else { + ireg = sdio_readb(card->func, host_intstatus_reg, &ret); + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n", + ret); + goto done; + } + } + if (ireg != 0) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * Clear the interrupt status register and re-enable + * the interrupt + */ + PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name, + ireg); + priv->adapter->irq_recv = ireg; + if (priv->card_type == CARD_TYPE_SD8777 || + priv->card_type == CARD_TYPE_SD8787) { + sdio_writeb(card->func, + ~(ireg) & (DN_LD_HOST_INT_STATUS | + UP_LD_HOST_INT_STATUS), + host_intstatus_reg, &ret); + if (ret) { + PRINTM(ERROR, + "BT: sdio_write_ioreg: clear int status register failed\n"); + goto done; + } + } + } else { + PRINTM(ERROR, "BT: ERR: ireg=0\n"); + } + OS_INT_DISABLE; + priv->adapter->sd_ireg |= ireg; + OS_INT_RESTORE; + bt_interrupt(m_dev); +done: + LEAVE(); +} + +/** + * @brief This function checks if the interface is ready to download + * or not while other download interfaces are present + * + * @param priv A pointer to bt_private structure + * @param val Winner status (0: winner) + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_check_winner_status(bt_private *priv, u8 *val) +{ + + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = priv->psdio_device->reg->card_fw_status0; + + ENTER(); + winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret); + if (ret != BT_STATUS_SUCCESS) { + LEAVE(); + return BT_STATUS_FAILURE; + } + *val = winner; + + LEAVE(); + return ret; +} + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** @brief This function tells lower driver that BT is suspended + * + * @param priv A pointer to bt_private structure + * @return None + */ +void +bt_is_suspended(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + priv->adapter->is_suspended = TRUE; + sdio_func_suspended(card->func); +} +#endif + +/** @brief This function handles client driver suspend + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +bt_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name); + mbt_hci_suspend_dev(m_dev); + skb_queue_purge(&priv->adapter->tx_queue); + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv, FALSE)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) { + PRINTM(CMD, "BT: HS not actived, suspend fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, FALSE)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to suspend!\n"); + } + } + } + + priv->adapter->is_suspended = TRUE; + +#ifdef SDIO_OOB_IRQ + mrvl_sdio_suspend(func); +#endif + LEAVE(); + /* We will keep the power when hs enabled successfully */ + if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) { +#ifdef MMC_PM_SKIP_RESUME_PROBE + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and " + "MMC_PM_SKIP_RESUME_PROBE\n"); + return sdio_set_host_pm_flags(func, + MMC_PM_KEEP_POWER | + MMC_PM_SKIP_RESUME_PROBE); +#else + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n"); + return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +#endif + } else { + PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n"); + return BT_STATUS_SUCCESS; + } +} + +void +bt_sdio_shutdown(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is shutdown\n", + sdio_func_id(func)); + return; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return; + } + + priv = cardp->priv; + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv, TRUE)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) { + PRINTM(CMD, "BT: HS not actived, shutdown fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv, TRUE)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to shutdown!\n"); + } + } + } + LEAVE(); +} + +/** @brief This function handles client driver resume + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS + */ +int +bt_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + + ENTER(); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; +#ifdef BLE_WAKEUP + if (priv->ble_wakeup_buf) { + + if (priv->adapter->is_suspended == TRUE) { + PRINTM(CMD, "BT, send bt_send_hw_remove_event\n"); + bt_send_hw_remove_event(priv); + } else { + PRINTM(CMD, "BT: Send system resume event\n");; + bt_send_system_event(priv, FALSE); + } + } +#endif + priv->adapter->is_suspended = FALSE; +#ifdef SDIO_OOB_IRQ + mrvl_sdio_resume(func); +#endif + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name); + mbt_hci_resume_dev(m_dev); + sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name); + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif +#endif + +/******************************************************** + Global Functions +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +static const struct dev_pm_ops bt_sdio_pm_ops = { + .suspend = bt_sdio_suspend, + .resume = bt_sdio_resume, +}; +#endif +#endif +static struct sdio_driver sdio_bt = { + .name = "sdio_bt", + .id_table = bt_ids, + .probe = sd_probe_card, + .remove = sd_remove_card, +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &bt_sdio_pm_ops, + .shutdown = bt_sdio_shutdown, + } +#endif +#endif +}; + +/** + * @brief This function registers the bt module in bus driver. + * + * @return An int pointer that keeps returned value + */ +int * +sbi_register(void) +{ + int *ret; + + ENTER(); + + if (sdio_register_driver(&sdio_bt) != 0) { + PRINTM(FATAL, "BT: SD Driver Registration Failed\n"); + LEAVE(); + return NULL; + } else + ret = (int *)1; + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the bt module in bus driver. + * + * @return N/A + */ +void +sbi_unregister(void) +{ + ENTER(); + sdio_unregister_driver(&sdio_bt); + LEAVE(); +} + +/** + * @brief This function registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_dev(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + u8 chiprev; + struct sdio_mmc_card *card = priv->bt_dev.card; + struct sdio_func *func; + u8 host_intstatus_reg = priv->psdio_device->reg->host_intstatus; + u8 host_int_rsr_reg = priv->psdio_device->reg->host_int_rsr_reg; + u8 card_misc_cfg_reg = priv->psdio_device->reg->card_misc_cfg_reg; + u8 card_revision_reg = priv->psdio_device->reg->card_revision; + u8 io_port_0_reg = priv->psdio_device->reg->io_port_0; + u8 io_port_1_reg = priv->psdio_device->reg->io_port_1; + u8 io_port_2_reg = priv->psdio_device->reg->io_port_2; + u8 card_magic_reg = CARD_MAGIC_REG; + u8 magic_val = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: Error: card or function is NULL!\n"); + goto failed; + } + func = card->func; + priv->hotplug_device = &func->dev; + + /* Initialize the private structure */ + strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name)); + priv->bt_dev.ioport = 0; + priv->bt_dev.fn = func->num; + + sdio_claim_host(func); +#ifdef SDIO_OOB_IRQ + ret = mrvl_sdio_claim_irq(func, sd_interrupt); +#else + ret = sdio_claim_irq(func, sd_interrupt); +#endif + if (ret) { + PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret); + goto release_host; + } + ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); + if (ret) { + PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__); + goto release_irq; + } + + /* read Revision Register to get the chip revision number */ + chiprev = sdio_readb(func, card_revision_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n"); + goto release_irq; + } + priv->adapter->chip_rev = chiprev; + PRINTM(INFO, "revision=%#x\n", chiprev); + + magic_val = sdio_readb(func, card_magic_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n"); + goto release_irq; + } + priv->adapter->magic_val = magic_val; + PRINTM(INFO, "magic_val=%#x\n", magic_val); + + /* + * Read the HOST_INTSTATUS_REG for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + reg = sdio_readb(func, host_intstatus_reg, &ret); + if (ret < 0) + goto release_irq; + + /* Read the IO port */ + reg = sdio_readb(func, io_port_0_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= reg; + + reg = sdio_readb(func, io_port_1_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 8); + + reg = sdio_readb(func, io_port_2_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 16); + + PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn, + priv->bt_dev.ioport); + + if (priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8978) { + if (bt_intmode == INT_MODE_GPIO) { + PRINTM(MSG, "Enable GPIO-1 INT\n"); + sdio_writeb(func, ENABLE_GPIO_1_INT_MODE, + SCRATCH_REG_32, &ret); + if (ret < 0) + goto release_irq; + } + } + +#define SDIO_INT_MASK 0x3F + if (priv->card_type == CARD_TYPE_SD8887 || + priv->card_type == CARD_TYPE_SD8897 || + priv->card_type == CARD_TYPE_SD8797 || + priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987 || + priv->card_type == CARD_TYPE_SD8978) { + /* Set Host interrupt reset to read to clear */ + reg = sdio_readb(func, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + /* Set auto re-enable */ + reg = sdio_readb(func, card_misc_cfg_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg, + &ret); + if (ret < 0) + goto release_irq; + } + + sdio_set_drvdata(func, card); + sdio_release_host(func); + + LEAVE(); + return BT_STATUS_SUCCESS; +release_irq: +#ifdef SDIO_OOB_IRQ + mrvl_sdio_release_irq(func); +#else + sdio_release_irq(func); +#endif +release_host: + sdio_release_host(func); +failed: + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function de-registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_unregister_dev(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + if (card && card->func) { + sdio_claim_host(card->func); +#ifdef SDIO_OOB_IRQ + mrvl_sdio_release_irq(card->func); +#else + sdio_release_irq(card->func); +#endif + sdio_disable_func(card->func); + sdio_release_host(card->func); + sdio_set_drvdata(card->func, NULL); + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_enable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sd_enable_host_int_mask(priv, HIM_ENABLE); + sd_get_rx_unit(priv); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_disable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sbi_disable_host_int_mask(priv, HIM_DISABLE); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param priv A pointer to bt_private structure + * @param payload A pointer to the data/cmd buffer + * @param nb Length of data/cmd + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + int ret = BT_STATUS_SUCCESS; + int buf_block_len; + int blksz; + int i = 0; + u8 *buf = NULL; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + buf = payload; + + blksz = SD_BLOCK_SIZE; + buf_block_len = (nb + blksz - 1) / blksz; + /* Allocate buffer and copy payload */ + if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) { + if (nb > MAX_TX_BUF_SIZE) { + PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb); + LEAVE(); + return BT_STATUS_FAILURE; + } + /* Ensure 8-byte aligned CMD buffer */ + buf = priv->adapter->tx_buf; + memcpy(buf, payload, nb); + } + sdio_claim_host(card->func); + do { + /* Transfer data to card */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: host_to_card, write iomem (%d) failed: %d\n", + i, ret); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) + goto exit; + } else { + PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n", + m_dev->name, nb); + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb); + } + } while (ret == BT_STATUS_FAILURE); + priv->bt_dev.tx_dnld_rdy = FALSE; +exit: + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function downloads firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_download_fw(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + sdio_claim_host(card->func); + if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) { + PRINTM(MSG, "BT: FW already downloaded!\n"); + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + /* Check if other interface is downloading */ + ret = sd_check_winner_status(priv, &winner); + if (ret == BT_STATUS_FAILURE) { + PRINTM(FATAL, "BT read winner status failed!\n"); + goto done; + } + if (winner) { + PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n", + winner); + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) { + PRINTM(FATAL, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + + do_gettimeofday(&priv->req_fw_time); + /* Download the main firmware via the helper firmware */ + if (sd_download_firmware_w_helper(priv)) { + PRINTM(INFO, "BT: FW download failed!\n"); + ret = BT_STATUS_FAILURE; + } + goto exit; +done: + sdio_release_host(card->func); +exit: + LEAVE(); + return ret; +err_register: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the interrupt status and handle it accordingly. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_get_int_status(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 sdio_ireg = 0; + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + OS_INT_DISABLE; + sdio_ireg = priv->adapter->sd_ireg; + priv->adapter->sd_ireg = 0; + OS_INT_RESTORE; + sdio_claim_host(card->func); + priv->adapter->irq_done = sdio_ireg; + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ + if (priv->bt_dev.tx_dnld_rdy) { /* tx_done already received */ + PRINTM(INFO, + "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", + priv->bt_dev.tx_dnld_rdy, sdio_ireg); + } else { + priv->bt_dev.tx_dnld_rdy = TRUE; + } + } + if (sdio_ireg & UP_LD_HOST_INT_STATUS) + sd_card_to_host(priv); + + ret = BT_STATUS_SUCCESS; + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function wakeup firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_wakeup_firmware(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + sdio_release_host(card->func); + PRINTM(CMD, "BT wake up firmware\n"); + + LEAVE(); + return ret; +} + +/** @brief This function updates the SDIO card types + * + * @param priv A Pointer to the bt_private structure + * @param card A Pointer to card + * + * @return N/A + */ +void +sdio_update_card_type(bt_private *priv, void *card) +{ + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card; + + /* Update card type */ + if (cardp->func->device == SD_DEVICE_ID_8777_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8777_BT_FN3) + priv->card_type = CARD_TYPE_SD8777; + else if (cardp->func->device == SD_DEVICE_ID_8787_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8787_BT_FN3) + priv->card_type = CARD_TYPE_SD8787; + else if (cardp->func->device == SD_DEVICE_ID_8887_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8887_BT_FN3) + priv->card_type = CARD_TYPE_SD8887; + else if (cardp->func->device == SD_DEVICE_ID_8897_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8897_BT_FN3) + priv->card_type = CARD_TYPE_SD8897; + else if (cardp->func->device == SD_DEVICE_ID_8797_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8797_BT_FN3) + priv->card_type = CARD_TYPE_SD8797; + else if (cardp->func->device == SD_DEVICE_ID_8977_BT_FN2) + priv->card_type = CARD_TYPE_SD8977; + else if (cardp->func->device == SD_DEVICE_ID_8978_BT_FN2) + priv->card_type = CARD_TYPE_SD8978; + else if (cardp->func->device == SD_DEVICE_ID_8997_BT_FN2) + priv->card_type = CARD_TYPE_SD8997; + else if (cardp->func->device == SD_DEVICE_ID_8987_BT_FN2 || + cardp->func->device == SD_DEVICE_ID_8987_BT_FN3) + priv->card_type = CARD_TYPE_SD8987; +} + +/** + * @brief This function get sdio device from card type + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +sdio_get_sdio_device(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 card_type = priv->card_type; + + ENTER(); + + switch (card_type) { + case CARD_TYPE_SD8777: + priv->psdio_device = &bt_sdio_sd8777; + break; + case CARD_TYPE_SD8787: + priv->psdio_device = &bt_sdio_sd8787; + break; + case CARD_TYPE_SD8887: + priv->psdio_device = &bt_sdio_sd8887; + break; + case CARD_TYPE_SD8897: + priv->psdio_device = &bt_sdio_sd8897; + break; + case CARD_TYPE_SD8797: + priv->psdio_device = &bt_sdio_sd8797; + break; + case CARD_TYPE_SD8977: + priv->psdio_device = &bt_sdio_sd8977; + break; + case CARD_TYPE_SD8978: + priv->psdio_device = &bt_sdio_sd8978; + break; + case CARD_TYPE_SD8997: + priv->psdio_device = &bt_sdio_sd8997; + break; + case CARD_TYPE_SD8987: + priv->psdio_device = &bt_sdio_sd8987; + break; + default: + PRINTM(ERROR, "BT can't get right card type \n"); + ret = BT_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +#define SD8897_INIT_START_REG 0xDC +#define SD8897_INIT_END_REG 0xE1 +#define SD8887_INIT_START_REG 0xA0 +#define SD8887_INIT_END_REG 0xA5 +#define SD8977_SD8978_SD8997_INIT_START_REG 0xF1 +#define SD8977_SD8978_SD8997_INIT_END_REG 0xF6 + +/** @brief This function dump the SDIO register + * + * @param priv A Pointer to the bt_private structure + * + * @return N/A + */ +void +bt_dump_sdio_regs(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + char buf[256], *ptr; + u8 loop, func, data; + unsigned int reg, reg_start, reg_end; + u8 index = 0; + unsigned int reg_table_8887[] = { 0x58, 0x59, 0x5c, 0x60, 0x64, 0x70, + 0x71, 0x72, 0x73, 0xd8, 0xd9, 0xda + }; + u8 loop_num = 0; + unsigned int *reg_table = NULL; + u8 reg_table_size = 0; + unsigned int init_reg_start = 0; + unsigned int init_reg_end = 0; + if (priv->card_type == CARD_TYPE_SD8887) { + init_reg_start = SD8887_INIT_START_REG; + init_reg_end = SD8887_INIT_END_REG; + } else if (priv->card_type == CARD_TYPE_SD8897) { + init_reg_start = SD8897_INIT_START_REG; + init_reg_end = SD8897_INIT_END_REG; + } else if (priv->card_type == CARD_TYPE_SD8977 || + priv->card_type == CARD_TYPE_SD8997 || + priv->card_type == CARD_TYPE_SD8987 || + priv->card_type == CARD_TYPE_SD8978) { + init_reg_start = SD8977_SD8978_SD8997_INIT_START_REG; + init_reg_end = SD8977_SD8978_SD8997_INIT_END_REG; + } + + if (priv->card_type == CARD_TYPE_SD8887) { + loop_num = 3; + reg_table = reg_table_8887; + reg_table_size = sizeof(reg_table_8887) / sizeof(int); + } else + loop_num = 2; + if (priv->adapter->ps_state) + sbi_wakeup_firmware(priv); + + for (loop = 0; loop < loop_num; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else if (loop == 2) { + /* Read specific registers of SDIO function1 */ + index = 0; + func = 2; + reg_start = reg_table[index++]; + reg_end = reg_table[reg_table_size - 1]; + } else { + func = 2; + reg_start = 0; + reg_end = 0x09; + } + if (loop == 2) + ptr += sprintf(ptr, "SDIO Func%d: ", func); + else + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, + reg_start, reg_end); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + ret = sd_f0_read_reg(priv, reg, &data); + else + ret = sd_read_reg(priv, reg, &data); + if (loop == 2) + ptr += sprintf(ptr, "(%#x)", reg); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + if (loop == 2 && reg < reg_end) + reg = reg_table[index++]; + else + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + + if (init_reg_start) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ", + init_reg_start, init_reg_end); + for (reg = init_reg_start; reg <= init_reg_end;) { + ret = sd_read_reg(priv, reg, &data); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + reg++; + } + PRINTM(MSG, "%s\n", buf); + } +} + +module_param(fw_name, charp, 0); +MODULE_PARM_DESC(fw_name, "Firmware name"); +module_param(bt_req_fw_nowait, int, 0); +MODULE_PARM_DESC(bt_req_fw_nowait, + "0: Use request_firmware API; 1: Use request_firmware_nowait API"); +module_param(multi_fn, int, 0); +MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;"); + +module_param(bt_intmode, int, 0); +MODULE_PARM_DESC(bt_intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO");
diff --git a/bt_sd8987/bt_char/hci_wrapper.h b/bt_sd8987/bt_char/hci_wrapper.h new file mode 100644 index 0000000..a780b69 --- /dev/null +++ b/bt_sd8987/bt_char/hci_wrapper.h
@@ -0,0 +1,173 @@ +/** @file hci_wrapper.h + * @brief This file contains HCI related definitions + * + * Copyright (C) 2011-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _HCI_WRAPPER_H_ +#define _HCI_WRAPPER_H_ + +#include <linux/module.h> +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +/** Define Seq num */ +#define BT_SEQ 0 +#define DEBUG_SEQ 3 + +/** Define dev type */ +#define BT_TYPE 1 +#define BT_AMP_TYPE 2 +#define DEBUG_TYPE 5 + +/** Define spec type */ +#define BLUEZ_SPEC 1 +#define IANYWHERE_SPEC 2 +#define GENERIC_SPEC 3 + +/** Define lock/unlock wrapper */ +#define mdev_req_lock(d) down(&d->req_lock) +#define mdev_req_unlock(d) up(&d->req_lock) + +/** Length of device name */ +#define DEV_NAME_LEN 32 + +/** Define struct m_dev */ +struct m_dev { + char name[DEV_NAME_LEN]; + int index; + unsigned long flags; + spinlock_t lock; + struct semaphore req_lock; + struct sk_buff_head rx_q; + wait_queue_head_t req_wait_q; + struct hci_dev_stats stat; + struct module *owner; + void *dev_pointer; + int dev_type; + int spec_type; + void *driver_data; + int wait_rx_complete; + int rx_complete_flag; + wait_queue_head_t rx_wait_q; + spinlock_t rxlock; + atomic_t extra_cnt; + + struct sk_buff *evt_skb; + struct sk_buff *acl_skb; + struct sk_buff *sco_skb; + + int (*open) (struct m_dev * m_dev); + int (*close) (struct m_dev * m_dev); + int (*flush) (struct m_dev * m_dev); + int (*send) (struct m_dev * m_dev, struct sk_buff * skb); + void (*destruct) (struct m_dev * m_dev); + void (*notify) (struct m_dev * m_dev, unsigned int evt); + int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg); + void (*query) (struct m_dev * m_dev, void *arg); + +}; + +/** Define struct mbt_dev */ +struct mbt_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; + __u8 type; + + __u16 pkt_type; + __u16 esco_type; + __u16 link_policy; + __u16 link_mode; + + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + + struct sk_buff *reassembly[3]; + + atomic_t promisc; +}; + +struct debug_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; +}; + +/** This function frees m_dev allocation */ +void free_m_dev(struct m_dev *m_dev); + +/** + * @brief This function receives frames + * + * @param skb A pointer to struct sk_buff + * @return 0--success otherwise error code + */ +static inline int +mdev_recv_frame(struct sk_buff *skb) +{ + struct m_dev *m_dev = (struct m_dev *)skb->dev; + if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags) + && !test_bit(HCI_INIT, &m_dev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Incomming skb */ + bt_cb(skb)->incoming = 1; + + /* Time stamp */ + __net_timestamp(skb); + + /* Put type byte before the data */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + + /* Queue frame for rx task */ + skb_queue_tail(&m_dev->rx_q, skb); + + /* Wakeup rx thread */ + wake_up_interruptible(&m_dev->req_wait_q); + + return 0; +} + +/** + * @brief mbt dev suspend handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_suspend_dev(struct m_dev *m_dev) +{ + return 0; +} + +/** + * @brief mbt dev resume handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_resume_dev(struct m_dev *m_dev) +{ + return 0; +} + +#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8987/bt_char/mbt_char.c b/bt_sd8987/bt_char/mbt_char.c new file mode 100644 index 0000000..e758427 --- /dev/null +++ b/bt_sd8987/bt_char/mbt_char.c
@@ -0,0 +1,797 @@ +/** @file mbt_char.c + * + * @brief This file contains the char device function calls + * + * Copyright (C) 2010-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/path.h> +#include <linux/namei.h> +#include <linux/mount.h> + +#include "bt_drv.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include <linux/sched/signal.h> +#endif +#include "mbt_char.h" + +#ifndef MIN +/** Find minimum value */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +static LIST_HEAD(char_dev_list); + +static DEFINE_SPINLOCK(char_dev_list_lock); + +static int mbtchar_major = MBTCHAR_MAJOR_NUM; + +/** + * @brief Gets char device structure + * + * @param dev A pointer to char_dev + * + * @return kobject structure + */ +struct kobject * +chardev_get(struct char_dev *dev) +{ + struct kobject *kobj; + + kobj = bt_priv_get(dev->m_dev->driver_data); + if (!kobj) + return NULL; + PRINTM(INFO, "dev get kobj\n"); + kobj = kobject_get(&dev->kobj); + if (!kobj) + bt_priv_put(dev->m_dev->driver_data); + return kobj; +} + +/** + * @brief Prints char device structure + * + * @param dev A pointer to char_dev + * + * @return N/A + */ +void +chardev_put(struct char_dev *dev) +{ + if (dev) { + struct m_dev *m_dev = dev->m_dev; + PRINTM(INFO, "dev put kobj\n"); + kobject_put(&dev->kobj); + if (m_dev) + bt_priv_put(m_dev->driver_data); + } +} + +/** + * @brief Changes permissions of the dev + * + * @param name pointer to character + * @param mode mode_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chmod(char *name, mode_t mode) +{ + struct path path; + struct inode *inode; + struct iattr newattrs; + int ret; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chmod(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief Changes ownership of the dev + * + * @param name pointer to character + * @param user uid_t type data + * @param group gid_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chown(char *name, uid_t user, gid_t group) +{ + struct path path; + struct inode *inode = NULL; + struct iattr newattrs; + int ret = 0; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chown(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + newattrs.ia_valid = ATTR_CTIME; + if (user != (uid_t) (-1)) { + newattrs.ia_valid |= ATTR_UID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_uid = user; +#else + newattrs.ia_uid = KUIDT_INIT(user); +#endif + } + if (group != (gid_t) (-1)) { + newattrs.ia_valid |= ATTR_GID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_gid = group; +#else + newattrs.ia_gid = KGIDT_INIT(group); +#endif + } + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief write handler for char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes written + */ +ssize_t +chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos) +{ + int nwrite = 0; + struct sk_buff *skb; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + if (!test_bit(HCI_UP, &m_dev->flags)) { + LEAVE(); + return -EBUSY; + } + nwrite = count; + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n"); + LEAVE(); + return -ENOMEM; + } + + if (copy_from_user((void *)skb_put(skb, count), buf, count)) { + PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n"); + kfree_skb(skb); + nwrite = -EFAULT; + goto exit; + } + + skb->dev = (void *)m_dev; + bt_cb(skb)->pkt_type = *((unsigned char *)skb->data); + skb_pull(skb, 1); + + PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len); + + /* Send skb to the hci wrapper layer */ + if (m_dev->send(m_dev, skb)) { + PRINTM(ERROR, "Write: Fail\n"); + nwrite = 0; + /* Send failed */ + kfree_skb(skb); + } +exit: + LEAVE(); + return nwrite; +} + +/** + * @brief read handler for BT char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes read + */ +ssize_t +chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos) +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + /* Wait for rx data */ + add_wait_queue(&m_dev->req_wait_q, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + skb = skb_dequeue(&m_dev->rx_q); + if (skb) + break; + if (!test_bit(HCI_UP, &m_dev->flags)) { + ret = -EBUSY; + break; + } + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&m_dev->req_wait_q, &wait); + + if (!skb) + goto out; + + PRINTM(DATA, "BT: chardev_read count=%d pkt_type: 0x%x, len=%d %p\n", + (int)count, bt_cb(skb)->pkt_type, skb->len, skb); + DBG_HEXDUMP(DAT_D, "chardev_read", skb->data,MIN((int)count,skb->len)); + if (skb->len > count) { + /* user data length is smaller than the skb length */ + if (copy_to_user(buf, skb->data, count)) { + ret = -EFAULT; + goto outf; + } + skb_pull(skb, count); + skb_queue_head(&m_dev->rx_q, skb); + wake_up_interruptible(&m_dev->req_wait_q); + ret = count; + goto out; + } else { + if (copy_to_user(buf, skb->data, skb->len)) { + ret = -EFAULT; + goto outf; + } + ret = skb->len; + PRINTM(DATA, "BT: chardev_read complete %p\n",skb); + } +outf: + kfree_skb(skb); +out: + if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) { + m_dev->rx_complete_flag = TRUE; + wake_up_interruptible(&m_dev->rx_wait_q); + } + LEAVE(); + return ret; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl common handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg) +#else +/** + * @brief ioctl common handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +char_ioctl(struct file *filp, unsigned int cmd, void *arg) +#endif +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { + case MBTCHAR_IOCTL_RELEASE: + m_dev->close(m_dev); + break; + case MBTCHAR_IOCTL_QUERY_TYPE: + m_dev->query(m_dev, arg); + break; + default: + m_dev->ioctl(m_dev, cmd, arg); + break; + } + LEAVE(); + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, (void *)arg); +#else + return char_ioctl(filp, cmd, (void *)arg); +#endif +} + +#ifdef CONFIG_COMPAT +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief compat ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl_compat(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief compat ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, compat_ptr(arg)); +#else + return char_ioctl(filp, cmd, compat_ptr(arg)); +#endif +} +#endif /* CONFIG_COMPAT */ + +/** + * @brief open handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = NULL; + struct m_dev *m_dev = NULL; + struct char_dev *cdev = NULL; + struct list_head *p = NULL; + ENTER(); + + list_for_each(p, &char_dev_list) { + cdev = list_entry(p, struct char_dev, list); + if (mbtchar_major == MAJOR(inode->i_cdev->dev) && + cdev->minor == MINOR(inode->i_cdev->dev)) { + dev = cdev; + break; + } + } + if (!dev) { + PRINTM(ERROR, "cannot find dev from inode\n"); + LEAVE(); + return -ENXIO; + } + if (!chardev_get(dev)) { + LEAVE(); + return -ENXIO; + } + filp->private_data = dev; /* for other methods */ + m_dev = dev->m_dev; + mdev_req_lock(m_dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (test_bit(HCI_UP, &m_dev->flags)) { + atomic_inc(&m_dev->extra_cnt); + goto done; + } +#endif + if (m_dev->open(m_dev)) { + ret = -EIO; + goto done; + } + set_bit(HCI_UP, &m_dev->flags); + +done: + mdev_req_unlock(m_dev); + if (ret) + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief release handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) { + LEAVE(); + return ret; + } +#endif + if (m_dev) + ret = dev->m_dev->close(dev->m_dev); + filp->private_data = NULL; + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief poll handler for char dev + * + * @param filp pointer to structure file + * @param wait pointer to poll_table structure + * @return mask + */ +static unsigned int +chardev_poll(struct file *filp, poll_table * wait) +{ + unsigned int mask; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + + m_dev = dev->m_dev; + poll_wait(filp, &m_dev->req_wait_q, wait); + mask = POLLOUT | POLLWRNORM; + if (skb_peek(&m_dev->rx_q)) + mask |= POLLIN | POLLRDNORM; + if (!test_bit(HCI_UP, &(m_dev->flags))) + mask |= POLLHUP; + PRINTM(INFO, "poll mask=0x%x\n", mask); + LEAVE(); + return mask; +} + +/* File ops for the Char driver */ +const struct file_operations chardev_fops = { + .owner = THIS_MODULE, + .read = chardev_read, + .write = chardev_write, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + .ioctl = chardev_ioctl, +#else + .unlocked_ioctl = chardev_ioctl, +#endif +#ifdef CONFIG_COMPAT + .compat_ioctl = chardev_ioctl_compat, +#endif + .open = chardev_open, + .release = chardev_release, + .poll = chardev_poll, +}; + +/** + * @brief This function creates the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param mod_name A pointer to char + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name) +{ + int ret = 0, dev_num; + unsigned long flags; + ENTER(); + /* create the chrdev region */ + if (mbtchar_major) { + dev_num = MKDEV(mbtchar_major, dev->minor); + ret = register_chrdev_region(dev_num, 1, mod_name); + } else { + PRINTM(INFO, "chardev: no major # yet\n"); + ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1, + mod_name); + } + + if (ret) { + PRINTM(ERROR, "chardev: create chrdev_region failed\n"); + LEAVE(); + return ret; + } + if (!mbtchar_major) { + /* Store the allocated dev major # */ + mbtchar_major = MAJOR(dev_num); + } + dev->cdev = cdev_alloc(); + dev->cdev->ops = &chardev_fops; + dev->cdev->owner = chardev_fops.owner; + dev_num = MKDEV(mbtchar_major, dev->minor); + + if (cdev_add(dev->cdev, dev_num, 1)) { + PRINTM(ERROR, "chardev: cdev_add failed\n"); + ret = -EFAULT; + goto free_cdev_region; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } + if (dev->dev_type == DEBUG_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } +#else + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } + if (dev->dev_type == DEBUG_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } +#endif + PRINTM(INFO, "register char dev=%s\n", dev_name); + + /** modify later */ + + spin_lock_irqsave(&char_dev_list_lock, flags); + list_add_tail(&dev->list, &char_dev_list); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + + LEAVE(); + return ret; +free_cdev_region: + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + LEAVE(); + return ret; +} + +/** + * @brief This function deletes the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name) +{ + ENTER(); + device_destroy(char_class, MKDEV(mbtchar_major, dev->minor)); + cdev_del(dev->cdev); + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + PRINTM(INFO, "unregister char dev=%s\n", dev_name); + + LEAVE(); + return 0; +} + +/** + * @brief This function cleans module + * + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup(struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + do { + dev = NULL; + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + unregister_char_dev(dev, char_class, dev->m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } while (dev); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + class_destroy(char_class); + LEAVE(); +} + +/** + * @brief This function cleans module + * + * @param m_dev A pointer to m_dev struct + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + if (dev->minor == m_dev->index) { + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + dev->m_dev = NULL; + unregister_char_dev(dev, char_class, m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } + spin_unlock_irqrestore(&char_dev_list_lock, flags); + LEAVE(); +}
diff --git a/bt_sd8987/bt_char/mbt_char.h b/bt_sd8987/bt_char/mbt_char.h new file mode 100644 index 0000000..6b34088 --- /dev/null +++ b/bt_sd8987/bt_char/mbt_char.h
@@ -0,0 +1,73 @@ +/** @file mbt_char.h + * + * @brief This file contains mbtchar driver specific defines etc + * + * Copyright (C) 2010-2019, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifndef __MBT_CHAR_H__ +#define __MBT_CHAR_H__ + +#include <linux/cdev.h> +#include <linux/device.h> + +/** Define ioctl */ +#define MBTCHAR_IOCTL_RELEASE _IO('M', 1) +#define MBTCHAR_IOCTL_QUERY_TYPE _IO('M', 2) +#ifdef BLE_WAKEUP +#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4) +#define MBTCHAR_IOCTL_BLE_GET_WHITELIST _IO('M', 5) +#endif + +#define MBTCHAR_MAJOR_NUM (0) + +/** Interface specific macros */ +#define MBTCHAR_MINOR_BASE (0) +#define FMCHAR_MINOR_BASE (10) +#define NFCCHAR_MINOR_BASE (20) +#define DEBUGCHAR_MINOR_BASE (30) + +/** Declaration of char_dev struct */ +struct char_dev { + struct list_head list; + int minor; + int dev_type; + struct cdev *cdev; + struct m_dev *m_dev; + struct kobject kobj; +}; + +/** Changes permissions of the dev */ +int mbtchar_chmod(char *name, mode_t mode); + +/** Changes ownership of the dev */ +int mbtchar_chown(char *name, uid_t user, gid_t group); + +/** This function creates the char dev */ +int register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name); + +/** This function deletes the char dev */ +int unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name); + +/** This function cleans module */ +void chardev_cleanup(struct class *char_class); + +/** This function cleans module */ +void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class); + +#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8997/Makefile b/bt_sd8997/Makefile new file mode 100644 index 0000000..ada84eb --- /dev/null +++ b/bt_sd8997/Makefile
@@ -0,0 +1,194 @@ +# File: Makefile +# Copyright (C) 2007-2018, Marvell International Ltd. +# + +CC= $(CROSS_COMPILE)gcc +LD= $(CROSS_COMPILE)ld + +BACKUP= /root/backup +YMD= `date +%Y%m%d%H%M` + +############################################################################# +# Configuration Options +############################################################################# + +# Debug Option +# DEBUG LEVEL n/1/2: +# n: NO DEBUG +# 1: PRINTM(MSG,...), PRINTM(FATAL,...), PRINTM(WARN,...) and PRINTM(INFO,...) +# 2: All PRINTM() +CONFIG_DEBUG=1 + + +# SDIO suspend/resume +CONFIG_SDIO_SUSPEND_RESUME=y + + +CONFIG_BLE_WAKEUP=y + +############################################################################# +# Select Platform Tools +############################################################################# + +MODEXT = ko + +ifeq ($(CONFIG_64BIT), y) + EXTRA_CFLAGS += -DMBT_64BIT +endif + +ifeq ($(CONFIG_T50), y) + EXTRA_CFLAGS += -DT50 + EXTRA_CFLAGS += -DT40 + EXTRA_CFLAGS += -DT3T +endif + +ifeq ($(CONFIG_BLE_WAKEUP), y) + EXTRA_CFLAGS += -DBLE_WAKEUP +endif + + + + +KERNELDIR ?= /usr/src/arm/linux-4.9.61-bg5ct +CROSS_COMPILE ?= /usr/local/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- + + +KERNELVERSION_X86 := $(shell uname -r) +KERNELDIR?=/lib/modules/$(KERNELVERSION_X86)/build + +EXTRA_CFLAGS += -I$(KERNELDIR)/include + +EXTRA_CFLAGS += -I$(M)/../mbtchar_src +EXTRA_CFLAGS += -I$(M)/bt +LD += -S + +#ifdef SD8xxx +BINDIR = ../bin_sd8xxx_btchar +#endif +BINDIR = ../bin_sd8997_btchar + + +############################################################################# +# Compiler Flags +############################################################################# + EXTRA_CFLAGS += -DFPNUM='"36"' + +ifeq ($(CONFIG_DEBUG),1) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 +endif + +ifeq ($(CONFIG_DEBUG),2) + EXTRA_CFLAGS += -DDEBUG_LEVEL1 + EXTRA_CFLAGS += -DDEBUG_LEVEL2 + DBG= -dbg +endif + +ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y) + EXTRA_CFLAGS += -DSDIO_SUSPEND_RESUME +endif + +############################################################################# +# Make Targets +############################################################################# + +BT_CHAR_OBJS = bt_char/bt_main.o bt_char/bt_sdiommc.o bt_char/bt_proc.o bt_char/mbt_char.o +BT_CHAR_OBJS += bt_char/bt_init.o + +BT_BLOCK_OBJS = bt/bt_main.o bt/bt_sdiommc.o bt/bt_proc.o bt/mbt_char.o +BT_BLOCK_OBJS += bt/bt_init.o + +ifneq ($(KERNELRELEASE),) + +ifeq ($(CONFIG_BERLIN_SDIO_BT_8997_CHAR_DRV),y) +BTOBJS = $(BT_CHAR_OBJS) +MODULE_LINK = y +endif +ifeq ($(CONFIG_BERLIN_SDIO_BT_8997_CHAR_DRV),m) +BTOBJS = $(BT_CHAR_OBJS) +MODULE_LINK = m +endif +ifeq ($(CONFIG_BERLIN_SDIO_BT_8997),y) +BTOBJS = $(BT_BLOCK_OBJS) +MODULE_LINK = y +endif +ifeq ($(CONFIG_BERLIN_SDIO_BT_8997),m) +BTOBJS = $(BT_BLOCK_OBJS) +MODULE_LINK = m +endif + +obj-$(MODULE_LINK) := bt8xxx.o +bt8xxx-objs := $(BTOBJS) + + + +# Otherwise we were called directly from the command line; invoke the kernel build system. +else +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules +endif + +############################################################### + +export CC LD EXTRA_CFLAGS KERNELDIR + +.PHONY: app/fm_app clean distclean + +app/fm_app: + $(MAKE) -C $@ + +echo: + +build: echo default + + @if [ ! -d $(BINDIR) ]; then \ + mkdir $(BINDIR); \ + fi + +ifeq ($(CONFIG_MULTI_INTERFACE), y) + cp -f mbt8xxx_sdio.$(MODEXT) $(BINDIR)/mbt8997_sdio$(DBG).$(MODEXT) +else + cp -f mbt8xxx.$(MODEXT) $(BINDIR)/mbt8997$(DBG).$(MODEXT) +endif + cp -r config $(BINDIR) + + + + + + cp -f README $(BINDIR) + + $(MAKE) -C app/fm_app $@ INSTALLDIR=$(BINDIR); + cp -f app/fm_app/fmapp $(BINDIR); + +clean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name "*.symvers" -exec rm {} \; + -find . -name "modules.order" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + $(MAKE) -C app/fm_app $@ + +install: default + +distclean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.orig" -exec rm {} \; + -find . -name "*.swp" -exec rm {} \; + -find . -name "*.*~" -exec rm {} \; + -find . -name "*~" -exec rm {} \; + -find . -name "*.d" -exec rm {} \; + -find . -name "*.a" -exec rm {} \; + -find . -name "tags" -exec rm {} \; + -find . -name ".*" -exec rm -rf 2> /dev/null \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions + $(MAKE) -C app/fm_app $@ +# End of file;
diff --git a/bt_sd8997/bt/bt_drv.h b/bt_sd8997/bt/bt_drv.h new file mode 100644 index 0000000..3a75eb0 --- /dev/null +++ b/bt_sd8997/bt/bt_drv.h
@@ -0,0 +1,858 @@ +/** @file bt_drv.h + * @brief This header file contains global constant/enum definitions, + * global variable declaration. + * + * Copyright (C) 2007-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_DRV_H_ +#define _BT_DRV_H_ + +#include <linux/version.h> +#include <linux/kthread.h> +#include <linux/skbuff.h> +#include <linux/vmalloc.h> + +#include "hci_wrapper.h" + +#ifndef BIT +/** BIT definition */ +#define BIT(x) (1UL << (x)) +#endif + +#ifdef MBT_64BIT +typedef u64 t_ptr; +#else +typedef u32 t_ptr; +#endif + +/** Define drv_mode bit */ +#define DRV_MODE_BT BIT(0) + +/** Define devFeature bit */ +#define DEV_FEATURE_BT BIT(0) +#define DEV_FEATURE_BTAMP BIT(1) +#define DEV_FEATURE_BLE BIT(2) + +/** Define maximum number of radio func supported */ +#define MAX_RADIO_FUNC 3 + +/** MAC address print format */ +#ifndef MACSTR +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +/** MAC address print arguments */ +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Debug level : Message */ +#define DBG_MSG BIT(0) +/** Debug level : Fatal */ +#define DBG_FATAL BIT(1) +/** Debug level : Error */ +#define DBG_ERROR BIT(2) +/** Debug level : Data */ +#define DBG_DATA BIT(3) +/** Debug level : Command */ +#define DBG_CMD BIT(4) +/** Debug level : Event */ +#define DBG_EVENT BIT(5) +/** Debug level : Interrupt */ +#define DBG_INTR BIT(6) + +/** Debug entry : Data dump */ +#define DBG_DAT_D BIT(16) +/** Debug entry : Data dump */ +#define DBG_CMD_D BIT(17) + +/** Debug level : Entry */ +#define DBG_ENTRY BIT(28) +/** Debug level : Warning */ +#define DBG_WARN BIT(29) +/** Debug level : Informative */ +#define DBG_INFO BIT(30) + +#ifdef DEBUG_LEVEL1 +extern u32 mbt_drvdbg; + +#ifdef DEBUG_LEVEL2 +/** Print informative message */ +#define PRINTM_INFO(msg...) \ + do {if (mbt_drvdbg & DBG_INFO) \ + printk(KERN_DEBUG msg); } while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) \ + do {if (mbt_drvdbg & DBG_WARN) \ + printk(KERN_DEBUG msg); } while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) \ + do {if (mbt_drvdbg & DBG_ENTRY) \ + printk(KERN_DEBUG msg); } while (0) +#else +/** Print informative message */ +#define PRINTM_INFO(msg...) do {} while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) do {} while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +/** Print interrupt message */ +#define PRINTM_INTR(msg...) \ + do {if (mbt_drvdbg & DBG_INTR) \ + printk(KERN_DEBUG msg); } while (0) +/** Print event message */ +#define PRINTM_EVENT(msg...) \ + do {if (mbt_drvdbg & DBG_EVENT) \ + printk(KERN_DEBUG msg); } while (0) +/** Print command message */ +#define PRINTM_CMD(msg...) \ + do {if (mbt_drvdbg & DBG_CMD) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data message */ +#define PRINTM_DATA(msg...) \ + do {if (mbt_drvdbg & DBG_DATA) \ + printk(KERN_DEBUG msg); } while (0) +/** Print error message */ +#define PRINTM_ERROR(msg...) \ + do {if (mbt_drvdbg & DBG_ERROR) \ + printk(KERN_ERR msg); } while (0) +/** Print fatal message */ +#define PRINTM_FATAL(msg...) \ + do {if (mbt_drvdbg & DBG_FATAL) \ + printk(KERN_ERR msg); } while (0) +/** Print message */ +#define PRINTM_MSG(msg...) \ + do {if (mbt_drvdbg & DBG_MSG) \ + printk(KERN_ALERT msg); } while (0) + +/** Print data dump message */ +#define PRINTM_DAT_D(msg...) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data dump message */ +#define PRINTM_CMD_D(msg...) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + printk(KERN_DEBUG msg); } while (0) + +/** Print message with required level */ +#define PRINTM(level, msg...) PRINTM_##level(msg) + +/** Debug dump buffer length */ +#define DBG_DUMP_BUF_LEN 64 +/** Maximum number of dump per line */ +#define MAX_DUMP_PER_LINE 16 +/** Maximum data dump length */ +#define MAX_DATA_DUMP_LEN 48 + +static inline void +hexdump(char *prompt, u8 *buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + printk(KERN_DEBUG "%s: len=%d\n", prompt, len); + for (i = 1; i <= len; i++) { + ptr += snprintf(ptr, 4, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +/** Debug hexdump of debug data */ +#define DBG_HEXDUMP_DAT_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + hexdump(x, y, z); } while (0) +/** Debug hexdump of debug command */ +#define DBG_HEXDUMP_CMD_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + hexdump(x, y, z); } while (0) + +/** Debug hexdump */ +#define DBG_HEXDUMP(level, x, y, z) DBG_HEXDUMP_##level(x, y, z) + +/** Mark entry point */ +#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +/** Mark exit point */ +#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +#else +/** Do nothing */ +#define PRINTM(level, msg...) do {} while (0) +/** Do nothing */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) +/** Do nothing */ +#define ENTER() do {} while (0) +/** Do nothing */ +#define LEAVE() do {} while (0) +#endif /* DEBUG_LEVEL1 */ + +/** Bluetooth upload size */ +#define BT_UPLD_SIZE 2312 +/** Bluetooth status success */ +#define BT_STATUS_SUCCESS (0) +/** Bluetooth status failure */ +#define BT_STATUS_FAILURE (-1) + +#ifndef TRUE +/** True value */ +#define TRUE 1 +#endif +#ifndef FALSE +/** False value */ +#define FALSE 0 +#endif + +/** Set thread state */ +#define OS_SET_THREAD_STATE(x) set_current_state(x) +/** Time to wait until Host Sleep state change in millisecond */ +#define WAIT_UNTIL_HS_STATE_CHANGED 2000 +/** Time to wait cmd resp in millisecond */ +#define WAIT_UNTIL_CMD_RESP 5000 + +/** Sleep until a condition gets true or a timeout elapses */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000)) +#else +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) +#endif + +typedef struct { + /** Task */ + struct task_struct *task; + /** Queue */ + wait_queue_head_t waitQ; + /** PID */ + pid_t pid; + /** Private structure */ + void *priv; +} bt_thread; + +static inline void +bt_activate_thread(bt_thread *thr) +{ + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitQ); + + /** Record the thread pid */ + thr->pid = current->pid; +} + +static inline void +bt_deactivate_thread(bt_thread *thr) +{ + thr->pid = 0; + return; +} + +static inline void +bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name) +{ + thr->task = kthread_run(btfunc, thr, "%s", name); +} + +static inline int +bt_terminate_thread(bt_thread *thr) +{ + /* Check if the thread is active or not */ + if (!thr->pid) + return -1; + + kthread_stop(thr->task); + return 0; +} + +static inline void +os_sched_timeout(u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + + schedule_timeout((millisec * HZ) / 1000); +} + +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__((packed)) +#endif + +/** BT histogram command */ +#define BT_CMD_HISTOGRAM 0xEA +/** max antenna num */ +#define MAX_ANTENNA_NUM 2 +/** BDR 1M */ +#define BDR_RATE_1M 1 +/** EDR 2/3 M */ +#define EDR_RATE_2_3M 2 +/** BLE 1M */ +#define BLE_RATE_1M 5 +/** max bt link number */ +#define MAX_BT_LINK 10 +/** max ble link number */ +#define MAX_BLE_LINK 16 + +typedef struct _bt_link_stat { + /** txrx rate 1: BDR_1M, 2:EDR 2/3 M, 3:BLE 1M */ + u8 txrxrate; + /** power: -30 = N = 20 dbm*/ + s8 txpower; + /** rssi: -127 to +20 (For BT), -128 to +127 (For BLE) */ + s8 rssi; +} __ATTRIB_PACK__ bt_link_stat; + +typedef struct _bt_histogram_data { + u8 antenna; + u8 powerclass; + bt_link_stat link[MAX_BT_LINK + MAX_BLE_LINK]; +} __ATTRIB_PACK__ bt_histogram_data; + +typedef struct _bt_hist_proc_data { + /** antenna */ + u8 antenna; + /** Private structure */ + struct _bt_private *pbt; +} bt_hist_proc_data; + +/** Data structure for the Marvell Bluetooth device */ +typedef struct _bt_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + + struct m_dev m_dev[MAX_RADIO_FUNC]; + + /** Tx download ready flag */ + u8 tx_dnld_rdy; + /** Function */ + u8 fn; + /** Rx unit */ + u8 rx_unit; + /** Power Save mode : Timeout configuration */ + u16 idle_timeout; + /** Power Save mode */ + u8 psmode; + /** Power Save command */ + u8 pscmd; + /** Host Sleep mode */ + u8 hsmode; + /** Host Sleep command */ + u8 hscmd; + /** Low byte is gap, high byte is GPIO */ + u16 gpio_gap; + /** Host Sleep configuration command */ + u8 hscfgcmd; + /** Host Send Cmd Flag */ + u8 sendcmdflag; + /** opcode for Send Cmd */ + u16 send_cmd_opcode; + /** Device Type */ + u8 devType; + /** Device Features */ + u8 devFeature; + /** cmd52 function */ + u8 cmd52_func; + /** cmd52 register */ + u8 cmd52_reg; + /** cmd52 value */ + u8 cmd52_val; + /** SDIO pull control command */ + u8 sdio_pull_ctrl; + /** Low 2 bytes is pullUp, high 2 bytes for pull-down */ + u32 sdio_pull_cfg; + /** Test mode command */ + u8 test_mode; +} bt_dev_t, *pbt_dev_t; + +typedef struct _bt_adapter { + /** Chip revision ID */ + u8 chip_rev; + /** magic val */ + u8 magic_val; + /** Surprise removed flag */ + u8 SurpriseRemoved; + /** IRQ number */ + int irq; + /** Interrupt counter */ + u32 IntCounter; + /** Tx packet queue */ + struct sk_buff_head tx_queue; + /** Pending Tx packet queue */ + struct sk_buff_head pending_queue; + /** tx lock flag */ + u8 tx_lock; + /** Power Save mode */ + u8 psmode; + /** Power Save state */ + u8 ps_state; + /** Host Sleep state */ + u8 hs_state; + /** hs skip count */ + u32 hs_skip; + /** suspend_fail flag */ + u8 suspend_fail; + /** suspended flag */ + u8 is_suspended; + /** Number of wakeup tries */ + u8 WakeupTries; + /** Host Sleep wait queue */ + wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__; + /** Host Cmd complet state */ + u8 cmd_complete; + /** last irq recv */ + u8 irq_recv; + /** last irq processed */ + u8 irq_done; + /** sdio int status */ + u8 sd_ireg; + /** buf allocated for transmit */ + u8 *tx_buffer; + /** buf for transmit */ + u8 *tx_buf; + /** buf allocated for read interrupt status */ + u8 *hw_regs_buf; + /** buf for read interrupt status */ + u8 *hw_regs; + /** tx pending */ + u32 skb_pending; +/** Version string buffer length */ +#define MAX_VER_STR_LEN 128 + /** Driver version */ + u8 drv_ver[MAX_VER_STR_LEN]; + /** Number of command timeout */ + u32 num_cmd_timeout; +} bt_adapter, *pbt_adapter; + +/** Length of prov name */ +#define PROC_NAME_LEN 32 + +struct item_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** Size */ + u32 size; + /** Address */ + t_ptr addr; + /** Offset */ + u32 offset; + /** Flag */ + u32 flag; +}; + +struct proc_private_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** File flag */ + u32 fileflag; + /** Buffer size */ + u32 bufsize; + /** Number of items */ + u32 num_items; + /** Item data */ + struct item_data *pdata; + /** Private structure */ + struct _bt_private *pbt; + /** File operations */ + const struct file_operations *fops; +}; + +struct device_proc { + /** Proc directory entry */ + struct proc_dir_entry *proc_entry; + /** proc entry for hist */ + struct proc_dir_entry *hist_entry; + /** num of proc files */ + u8 num_proc_files; + /** pointer to proc_private_data */ + struct proc_private_data *pfiles; +}; + +/** Private structure for the MV device */ +typedef struct _bt_private { + /** Bluetooth device */ + bt_dev_t bt_dev; + /** Adapter */ + bt_adapter *adapter; + /** Firmware helper */ + const struct firmware *fw_helper; + /** Firmware */ + const struct firmware *firmware; + /** Init user configure file */ + const struct firmware *init_user_cfg; + /** Init user configure wait queue token */ + u16 init_user_conf_wait_flag; + /** Init user configure file wait queue */ + wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__; + /** Firmware request start time */ + struct timeval req_fw_time; + /** Hotplug device */ + struct device *hotplug_device; + /** thread to service interrupts */ + bt_thread MainThread; + /** proc data */ + struct device_proc dev_proc[MAX_RADIO_FUNC]; + /** Driver lock */ + spinlock_t driver_lock; + /** Driver lock flags */ + ulong driver_flags; + /** Driver reference flags */ + struct kobject kobj; + u8 fw_reload; + /* hist_data_len */ + u8 hist_data_len; + /** hist data */ + bt_histogram_data hist_data[MAX_ANTENNA_NUM]; + /** hist proc data */ + bt_hist_proc_data hist_proc[MAX_ANTENNA_NUM]; +#ifdef BLE_WAKEUP + u8 ble_wakeup_buf_size; + u8 *ble_wakeup_buf; +#endif +} bt_private, *pbt_private; + +int bt_get_histogram(bt_private *priv); + +/** Disable interrupt */ +#define OS_INT_DISABLE spin_lock_irqsave(&priv->driver_lock, \ + priv->driver_flags) +/** Enable interrupt */ +#define OS_INT_RESTORE spin_unlock_irqrestore(&priv->driver_lock, \ + priv->driver_flags) + +#ifndef HCI_BT_AMP +/** BT_AMP flag for device type */ +#define HCI_BT_AMP 0x80 +#endif + +/** Device type of BT */ +#define DEV_TYPE_BT 0x00 +/** Device type of AMP */ +#define DEV_TYPE_AMP 0x01 + +/** Marvell vendor packet */ +#define MRVL_VENDOR_PKT 0xFE + +/** Bluetooth command : Get FW Version */ +#define BT_CMD_GET_FW_VERSION 0x0F +/** Bluetooth command : Sleep mode */ +#define BT_CMD_AUTO_SLEEP_MODE 0x23 +/** Bluetooth command : Host Sleep configuration */ +#define BT_CMD_HOST_SLEEP_CONFIG 0x59 +/** Bluetooth command : Host Sleep enable */ +#define BT_CMD_HOST_SLEEP_ENABLE 0x5A +/** Bluetooth command : Module Configuration request */ +#define BT_CMD_MODULE_CFG_REQ 0x5B + +/** Bluetooth command : PMIC Configure */ +#define BT_CMD_PMIC_CONFIGURE 0x7D + +/** Bluetooth command : SDIO pull up down configuration request */ +#define BT_CMD_SDIO_PULL_CFG_REQ 0x69 +/** Bluetooth command : Set Evt Filter Command */ +#define BT_CMD_SET_EVT_FILTER 0x05 +/** Bluetooth command : Enable Write Scan Command */ +#define BT_CMD_ENABLE_WRITE_SCAN 0x1A +/** Bluetooth command : Enable Device under test mode */ +#define BT_CMD_ENABLE_DEVICE_TESTMODE 0x03 +/** Sub Command: Module Bring Up Request */ +#define MODULE_BRINGUP_REQ 0xF1 +/** Sub Command: Module Shut Down Request */ +#define MODULE_SHUTDOWN_REQ 0xF2 +/** Module already up */ +#define MODULE_CFG_RESP_ALREADY_UP 0x0c +/** Sub Command: Host Interface Control Request */ +#define MODULE_INTERFACE_CTRL_REQ 0xF5 + +/** Bluetooth event : Power State */ +#define BT_EVENT_POWER_STATE 0x20 + +/** Bluetooth Power State : Enable */ +#define BT_PS_ENABLE 0x02 +/** Bluetooth Power State : Disable */ +#define BT_PS_DISABLE 0x03 +/** Bluetooth Power State : Sleep */ +#define BT_PS_SLEEP 0x01 +/** Bluetooth Power State : Awake */ +#define BT_PS_AWAKE 0x02 + +/** Vendor OGF */ +#define VENDOR_OGF 0x3F +/** OGF for reset */ +#define RESET_OGF 0x03 +/** Bluetooth command : Reset */ +#define BT_CMD_RESET 0x03 + +/** Host Sleep activated */ +#define HS_ACTIVATED 0x01 +/** Host Sleep deactivated */ +#define HS_DEACTIVATED 0x00 + +/** Power Save sleep */ +#define PS_SLEEP 0x01 +/** Power Save awake */ +#define PS_AWAKE 0x00 + +/** bt header length */ +#define BT_HEADER_LEN 4 + +#ifndef MAX +/** Return maximum of two */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/** This is for firmware specific length */ +#define EXTRA_LEN 36 + +/** Command buffer size for Marvell driver */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Bluetooth Rx packet buffer size for Marvell driver */ +#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \ + (HCI_MAX_FRAME_SIZE + EXTRA_LEN) + +/** Buffer size to allocate */ +#define ALLOC_BUF_SIZE (((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \ + MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \ + + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) + +/** Request FW timeout in second */ +#define REQUEST_FW_TIMEOUT 30 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** The number of times to try when waiting for downloaded firmware to + become active when multiple interface is present */ +#define MAX_MULTI_INTERFACE_POLL_TRIES 150 + +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** default idle time */ +#define DEFAULT_IDLE_TIME 1000 + +#define BT_CMD_HEADER_SIZE 3 + +typedef struct _BT_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** Data */ + u8 data[128]; +} __ATTRIB_PACK__ BT_CMD; + +typedef struct _BT_EVENT { + /** Event Counter */ + u8 EC; + /** Length */ + u8 length; + /** Data */ + u8 data[8]; +} BT_EVENT; + +#if defined(SDIO_SUSPEND_RESUME) +#define DEF_GPIO_GAP 0xffff +#endif + +#ifdef BLE_WAKEUP +#define BD_ADDR_SIZE 6 +/** Vendor specific event */ +#define VENDOR_SPECIFIC_EVENT 0xff +/** system suspend event */ +#define HCI_SYSTEM_SUSPEND_EVT 0x80 +/** system suspend */ +#define HCI_SYSTEM_SUSPEND 0x00 +/** system resume */ +#define HCI_SYSTEM_RESUME 0x01 +/** This function enables ble wake up pattern */ +int bt_config_ble_wakeup(bt_private *priv); +int bt_send_system_event(bt_private *priv, u8 flag); +#endif + +/** This function verify the received event pkt */ +int check_evtpkt(bt_private *priv, struct sk_buff *skb); + +/* Prototype of global function */ +/** This function gets the priv reference */ +struct kobject *bt_priv_get(bt_private *priv); +/** This function release the priv reference */ +void bt_priv_put(bt_private *priv); +/** This function adds the card */ +bt_private *bt_add_card(void *card); +/** This function removes the card */ +int bt_remove_card(void *card); +/** This function handles the interrupt */ +void bt_interrupt(struct m_dev *m_dev); + +/** This function creates proc interface directory structure */ +int bt_root_proc_init(void); +/** This function removes proc interface directory structure */ +int bt_root_proc_remove(void); +/** This function initializes proc entry */ +int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq); +/** This function removes proc interface */ +void bt_proc_remove(bt_private *priv); + +/** This function process the received event */ +int bt_process_event(bt_private *priv, struct sk_buff *skb); +/** This function enables host sleep */ +int bt_enable_hs(bt_private *priv); +/** This function used to send command to firmware */ +int bt_prepare_command(bt_private *priv); +/** This function frees the structure of adapter */ +void bt_free_adapter(bt_private *priv); + +/** clean up m_devs */ +void clean_up_m_devs(bt_private *priv); +/** bt driver call this function to register to bus driver */ +int *sbi_register(void); +/** bt driver call this function to unregister to bus driver */ +void sbi_unregister(void); +/** bt driver calls this function to register the device */ +int sbi_register_dev(bt_private *priv); +/** bt driver calls this function to unregister the device */ +int sbi_unregister_dev(bt_private *priv); +/** This function initializes firmware */ +int sbi_download_fw(bt_private *priv); +/** Configures hardware to quit deep sleep state */ +int sbi_wakeup_firmware(bt_private *priv); +/** Module configuration and register device */ +int sbi_register_conf_dpc(bt_private *priv); + +/** This function is used to send the data/cmd to hardware */ +int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb); +/** This function reads the current interrupt status register */ +int sbi_get_int_status(bt_private *priv); + +/** bt fw reload flag */ +extern int bt_fw_reload; +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 +/** This function reload firmware */ +void bt_request_fw_reload(bt_private *priv, int mode); +/** This function enables the host interrupts */ +int sd_enable_host_int(bt_private *priv); +/** This function disables the host interrupts */ +int sd_disable_host_int(bt_private *priv); +#define MAX_TX_BUF_SIZE 2312 +/** This function downloads firmware image to the card */ +int sd_download_firmware_w_helper(bt_private *priv); +void bt_dump_sdio_regs(bt_private *priv); +/* dumps the firmware to /var/ or /data/ */ +void bt_dump_firmware_info_v2(bt_private *priv); + +/** Max line length allowed in init config file */ +#define MAX_LINE_LEN 256 +/** Max MAC address string length allowed */ +#define MAX_MAC_ADDR_LEN 18 +/** Max register type/offset/value etc. parameter length allowed */ +#define MAX_PARAM_LEN 12 + +/** Bluetooth command : Mac address configuration */ +#define BT_CMD_CONFIG_MAC_ADDR 0x22 +/** Bluetooth command : Write CSU register */ +#define BT_CMD_CSU_WRITE_REG 0x66 +/** Bluetooth command : Load calibrate data */ +#define BT_CMD_LOAD_CONFIG_DATA 0x61 +/** Bluetooth command : Load calibrate ext data */ +#define BT_CMD_LOAD_CONFIG_DATA_EXT 0x60 + +/** Bluetooth command : BLE deepsleep */ +#define BT_CMD_BLE_DEEP_SLEEP 0x8b + +typedef struct _BT_BLE_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** deepsleep flag */ + u8 deepsleep; +} __ATTRIB_PACK__ BT_BLE_CMD; + +typedef struct _BT_CSU_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** reg type */ + u8 type; + /** address */ + u8 offset[4]; + /** Data */ + u8 value[2]; +} __ATTRIB_PACK__ BT_CSU_CMD; + +/** This function sets mac address */ +int bt_set_mac_address(bt_private *priv, u8 *mac); +/** This function writes value to CSU registers */ +int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value); +/** BT set user defined init data and param */ +int bt_init_config(bt_private *priv, char *cfg_file); +/** BT PMIC Configure command */ +int bt_pmic_configure(bt_private *priv); +/** This function load the calibrate data */ +int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac); +/** This function load the calibrate ext data */ +int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len); +/** BT set user defined calibration data */ +int bt_cal_config(bt_private *priv, char *cfg_file, char *mac); +/** BT set user defined calibration ext data */ +int bt_cal_config_ext(bt_private *priv, char *cfg_file); +int bt_init_mac_address(bt_private *priv, char *mac); + +int bt_set_independent_reset(bt_private *priv); +/** Bluetooth command : Independent reset */ +#define BT_CMD_INDEPENDENT_RESET 0x0D + +typedef struct _BT_HCI_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** cmd type */ + u8 cmd_type; + /** cmd len */ + u8 cmd_len; + /** Data */ + u8 data[6]; +} __ATTRIB_PACK__ BT_HCI_CMD; + +#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8997/bt/bt_init.c b/bt_sd8997/bt/bt_init.c new file mode 100644 index 0000000..498c0de --- /dev/null +++ b/bt_sd8997/bt/bt_init.c
@@ -0,0 +1,751 @@ +/** @file bt_init.c + * + * @brief This file contains the init functions for BlueTooth + * driver. + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/string.h> +#include <linux/firmware.h> + +#include "bt_drv.h" + +extern int bt_req_fw_nowait; + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) + +#define isdigit(c) (('0' <= (c) && (c) <= '9')) +#define isspace(c) (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9))) +/** + * @brief Returns hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +static int +bt_hexval(char chr) +{ + ENTER(); + + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + LEAVE(); + return 0; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +static char * +bt_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Returns hex value of a given ascii string + * + * @param a String to be converted + * + * @return hex value + */ +static int +bt_atox(const char *a) +{ + int i = 0; + ENTER(); + while (isxdigit(*a)) + i = i * 16 + bt_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Converts mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +static void +bt_mac2u8(u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + memcpy(mac_buff, buf, strlen(buf)); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = bt_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = bt_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +/** + * @brief Returns integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_atoi(int *data, char *a) +{ + int i, val = 0, len; + + ENTER(); + + len = strlen(a); + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = bt_atox(a); + return BT_STATUS_SUCCESS; + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + PRINTM(ERROR, "Invalid char %c in string %s\n", a[i], + a); + return BT_STATUS_FAILURE; + } + } + *data = val; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief parse cal-data + * + * @param src a pointer to cal-data string + * @param len len of cal-data + * @param dst a pointer to return cal-data + * @param dst_size size of dest buffer + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size) +{ + const u8 *ptr; + u8 *dptr; + u32 count = 0; + int ret = BT_STATUS_FAILURE; + + ENTER(); + ptr = src; + dptr = dst; + + while ((ptr - src) < len) { + if (*ptr && isspace(*ptr)) { + ptr++; + continue; + } + + if (isxdigit(*ptr)) { + if ((dptr - dst) >= *dst_size) { + PRINTM(ERROR, "cal_file size too big!!!\n"); + goto done; + } + *dptr++ = bt_atox((const char *)ptr); + ptr += 2; + count++; + } else { + ptr++; + } + } + if (dptr == dst) { + ret = BT_STATUS_FAILURE; + goto done; + } + + *dst_size = count; + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief BT get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return -1 or length of the line + */ +int +parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos) +{ + static s32 pos; + u8 *src, *dest; + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while (pos < size && *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + *dest = '\0'; + /* parse new line */ + pos++; + return strlen((const char *)line_pos); +} + +/** + * @brief BT parse ASCII format data to MAC address + * + * @param priv BT private handle + * @param data Source data + * @param size data length + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_init_cfg(bt_private *priv, u8 *data, u32 size) +{ + u8 *pos; + u8 *intf_s, *intf_e; + u8 s[MAX_LINE_LEN]; /* 1 line data */ + u32 line_len; + char dev_name[MAX_PARAM_LEN]; + u8 buf[MAX_PARAM_LEN]; + u8 bt_addr[MAX_MAC_ADDR_LEN]; + u8 bt_mac[ETH_ALEN]; + int setting = 0; + u8 type = 0; + u16 value = 0; + u32 offset = 0; + int ret = BT_STATUS_FAILURE; + + memset(dev_name, 0, sizeof(dev_name)); + memset(bt_addr, 0, sizeof(bt_addr)); + memset(bt_mac, 0, sizeof(bt_mac)); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Need n't process this line */ + + /* Process MAC addr */ + if (strncmp((char *)pos, "mac_addr", 8) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(dev_name, (const char *)intf_s + 1, + intf_e - intf_s - 1); + dev_name[intf_e - intf_s - 1] = '\0'; + strncpy((char *)bt_addr, + (const char *)intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + /* Convert MAC format */ + bt_mac2u8(bt_mac, (char *)bt_addr); + PRINTM(CMD, + "HCI: %s new BT Address " MACSTR "\n", + dev_name, MAC2STR(bt_mac)); + if (BT_STATUS_SUCCESS != + bt_set_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp((char *)pos, "bt_reg", 6) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s + 1, + 1); + buf[1] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + type = (u8)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg type\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = (u8 *)strchr((const char *)intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, + intf_e - intf_s); + buf[intf_e - intf_s] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + offset = (u32)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg offset\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) { + PRINTM(ERROR, + "BT: Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, sizeof(buf)); + if (0 == bt_atoi(&setting, (char *)buf)) + value = (u16) setting; + else { + PRINTM(ERROR, "BT: Fail to parse reg value\n"); + goto done; + } + + PRINTM(CMD, + "BT: Write reg type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + if (BT_STATUS_SUCCESS != + bt_write_reg(priv, type, offset, value)) { + PRINTM(FATAL, + "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + goto done; + } + } + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to bt_private structure + * + * @return N/A + */ +static void +bt_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + bt_private *priv = (bt_private *)context; + + ENTER(); + + if (!firmware) + PRINTM(ERROR, "BT user init config request firmware failed\n"); + + priv->init_user_cfg = firmware; + priv->init_user_conf_wait_flag = TRUE; + wake_up_interruptible(&priv->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief BT set user defined init data and param + * + * @param priv BT private handle + * @param cfg_file user cofig file + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_config(bt_private *priv, char *cfg_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cfg) + ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + u8 cal_data[32]; + u8 *mac_data = NULL; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + u8 *pcal_data = cal_data; + + memset(bt_mac, 0, sizeof(bt_mac)); + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (mac != NULL) { + /* Convert MAC format */ + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: new BT Address " MACSTR "\n", + MAC2STR(bt_mac)); + mac_data = bt_mac; + } + if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size) +{ + u8 cal_data[128]; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (BT_STATUS_SUCCESS != + bt_load_cal_data_ext(priv, cal_data, cal_data_len)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config(bt_private *priv, char *cal_file, char *mac) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config_ext(bt_private *priv, char *cal_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT init mac address from bt_mac parametre when insmod + * + * @param priv a pointer to bt_private structure + * @param bt_mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_mac_address(bt_private *priv, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + int ret = BT_STATUS_FAILURE; + + ENTER(); + memset(bt_mac, 0, sizeof(bt_mac)); + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac)); + ret = bt_set_mac_address(priv, bt_mac); + if (ret != BT_STATUS_SUCCESS) + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre.\n"); + + LEAVE(); + return ret; +}
diff --git a/bt_sd8997/bt/bt_main.c b/bt_sd8997/bt/bt_main.c new file mode 100644 index 0000000..dc225fc --- /dev/null +++ b/bt_sd8997/bt/bt_main.c
@@ -0,0 +1,3525 @@ +/** @file bt_main.c + * + * @brief This file contains the major functions in BlueTooth + * driver. It includes init, exit, open, close and main + * thread etc.. + * + * Copyright (C) 2007-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/** + * @mainpage M-BT Linux Driver + * + * @section overview_sec Overview + * + * The M-BT is a Linux reference driver for Marvell Bluetooth chipset. + * + * @section copyright_sec Copyright + * + * Copyright (C) 2007-2016, Marvell International Ltd. + * + */ + +#include <linux/module.h> + +#ifdef CONFIG_OF +#include <linux/of.h> +#endif + +#include <linux/mmc/sdio_func.h> + +#include "bt_drv.h" +#include "mbt_char.h" +#include "bt_sdio.h" + +/** Version */ +#define VERSION "C4X14113" + +/** Driver version */ +static char mbt_driver_version[] = "SD8997-%s-" VERSION "-(" "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** Declare and initialize fw_version */ +static char fw_version[32] = "0.0.0.p0"; + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ + +#define AID_NET_BT_STACK 3008 /* bluetooth stack */ + +/** Define module name */ + +#define MODULE_NAME "bt_fm_nfc" + +/** Declaration of chardev class */ +static struct class *chardev_class; + +/** Interface specific variables */ + +/** Default Driver mode */ +static int drv_mode = (DRV_MODE_BT); + +/** fw reload flag */ +int bt_fw_reload; +/** fw serial download flag */ +int bt_fw_serial = 1; + +/** Firmware flag */ +static int fw = 1; +/** default powermode */ +static int psmode = 1; +/** Init config file (MAC address, register etc.) */ +static char *init_cfg; +/** Calibration config file (MAC address, init powe etc.) */ +static char *cal_cfg; +/** Calibration config file EXT */ +static char *cal_cfg_ext; +/** Init MAC address */ +static char *bt_mac; +static int btindrst = -1; + +/** Setting mbt_drvdbg value based on DEBUG level */ +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff & ~DBG_EVENT) +#else +#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) +#endif /* DEBUG_LEVEL2 */ +u32 mbt_drvdbg = DEFAULT_DEBUG_MASK; +#endif + +#ifdef CONFIG_OF +static int dts_enable = 1; +#endif + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int mbt_pm_keep_power = 1; +#endif + +static int btpmic = 0; + +/** + * @brief Alloc bt device + * + * @return pointer to structure mbt_dev or NULL + */ +struct mbt_dev * +alloc_mbt_dev(void) +{ + struct mbt_dev *mbt_dev; + ENTER(); + + mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL); + if (!mbt_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return mbt_dev; +} + +/** + * @brief Frees m_dev + * + * @return N/A + */ +void +free_m_dev(struct m_dev *m_dev) +{ + ENTER(); + kfree(m_dev->dev_pointer); + m_dev->dev_pointer = NULL; + LEAVE(); +} + +/** + * @brief clean up m_devs + * + * @return N/A + */ +void +clean_up_m_devs(bt_private *priv) +{ + struct m_dev *m_dev = NULL; + struct hci_dev *hdev = NULL; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if (m_dev->spec_type == BLUEZ_SPEC) { + hdev = (struct hci_dev *)m_dev->dev_pointer; + /** check if dev->name has been assigned */ + if (strstr(hdev->name, "hci")) + hci_unregister_dev(hdev); + hci_free_dev(hdev); + } + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL; + } + LEAVE(); + return; +} + +/** + * @brief This function verify the received event pkt + * + * Event format: + * +--------+--------+--------+--------+--------+ + * | Event | Length | ncmd | Opcode | + * +--------+--------+--------+--------+--------+ + * | 1-byte | 1-byte | 1-byte | 2-byte | + * +--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +check_evtpkt(bt_private *priv, struct sk_buff *skb) +{ + struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data; + struct hci_ev_cmd_complete *ec; + u16 opcode, ocf; + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (!priv->bt_dev.sendcmdflag) { + ret = BT_STATUS_FAILURE; + goto exit; + } + if (hdr->evt == HCI_EV_CMD_COMPLETE) { + ec = (struct hci_ev_cmd_complete *) + (skb->data + HCI_EVENT_HDR_SIZE); + opcode = __le16_to_cpu(ec->opcode); + ocf = hci_opcode_ocf(opcode); + PRINTM(CMD, + "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n", + opcode, ocf, priv->bt_dev.send_cmd_opcode); + if (opcode != priv->bt_dev.send_cmd_opcode) { + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (ocf) { + case BT_CMD_MODULE_CFG_REQ: + case BT_CMD_BLE_DEEP_SLEEP: + case BT_CMD_CONFIG_MAC_ADDR: + case BT_CMD_CSU_WRITE_REG: + case BT_CMD_LOAD_CONFIG_DATA: + case BT_CMD_LOAD_CONFIG_DATA_EXT: + case BT_CMD_AUTO_SLEEP_MODE: + case BT_CMD_HOST_SLEEP_CONFIG: + case BT_CMD_SDIO_PULL_CFG_REQ: + case BT_CMD_SET_EVT_FILTER: + case BT_CMD_ENABLE_WRITE_SCAN: + // case BT_CMD_ENABLE_DEVICE_TESTMODE: + case BT_CMD_RESET: + case BT_CMD_PMIC_CONFIGURE: + case BT_CMD_INDEPENDENT_RESET: + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter->cmd_wait_q); + break; + case BT_CMD_GET_FW_VERSION: + { + u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + snprintf(fw_version, sizeof(fw_version), + "%u.%u.%u.p%u", pos[2], pos[1], pos[0], + pos[3]); + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } + case BT_CMD_HISTOGRAM: + { + u8 *status = + skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete); + u8 *pos = + (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + if (*status == 0) { + priv->hist_data_len = + hdr->plen - + sizeof(struct + hci_ev_cmd_complete) - 1; + if (priv->hist_data_len > + sizeof(priv->hist_data)) + priv->hist_data_len = + sizeof(priv->hist_data); + memcpy(priv->hist_data, pos, + priv->hist_data_len); + PRINTM(CMD, "histogram len=%d\n", + priv->hist_data_len); + } + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } + case BT_CMD_HOST_SLEEP_ENABLE: + priv->bt_dev.sendcmdflag = FALSE; + break; + default: + /** Ignore command not defined but send by driver */ + if (opcode == priv->bt_dev.send_cmd_opcode) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } else { + ret = BT_STATUS_FAILURE; + } + break; + } + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** + * @brief This function process the received event + * + * Event format: + * +--------+--------+--------+--------+-----+ + * | EC | Length | Data | + * +--------+--------+--------+--------+-----+ + * | 1-byte | 1-byte | n-byte | + * +--------+--------+--------+--------+-----+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_event(bt_private *priv, struct sk_buff *skb) +{ + int ret = BT_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + BT_EVENT *pevent; + + ENTER(); + pevent = (BT_EVENT *)skb->data; + if (pevent->EC != 0xff) { + PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC); + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (pevent->data[0]) { + case BT_CMD_HISTOGRAM: + break; + case BT_CMD_AUTO_SLEEP_MODE: + if (pevent->data[2] == BT_STATUS_SUCCESS) { + if (pevent->data[1] == BT_PS_ENABLE) + priv->adapter->psmode = 1; + else + priv->adapter->psmode = 0; + PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name, + (priv->adapter->psmode) ? "Enable" : "Disable"); + + } else { + PRINTM(CMD, "BT: PS Mode Command Fail %s\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_CONFIG: + if (pevent->data[3] == BT_STATUS_SUCCESS) { + PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n", + m_dev->name, pevent->data[1], pevent->data[2]); + } else { + PRINTM(CMD, "BT: %s: HSCFG Command Fail\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_ENABLE: + if (pevent->data[1] == BT_STATUS_SUCCESS) { + priv->adapter->hs_state = HS_ACTIVATED; + if (priv->adapter->suspend_fail == FALSE) { +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED + bt_is_suspended(priv); +#endif +#endif +#endif + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + if (priv->adapter->psmode) + priv->adapter->ps_state = PS_SLEEP; + PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n", + m_dev->name); + + } else { + PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name); + } + break; + case BT_CMD_MODULE_CFG_REQ: + if ((priv->bt_dev.sendcmdflag == TRUE) && + ((pevent->data[1] == MODULE_BRINGUP_REQ) + || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) { + if (pevent->data[1] == MODULE_BRINGUP_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2] && (pevent->data[2] != + MODULE_CFG_RESP_ALREADY_UP)) + ? "Bring up Fail" : "Bring up success"); + priv->bt_dev.devType = pevent->data[3]; + PRINTM(CMD, "devType:%s\n", + (pevent->data[3] == + DEV_TYPE_AMP) ? "AMP controller" : + "BR/EDR controller"); + priv->bt_dev.devFeature = pevent->data[4]; + PRINTM(CMD, "devFeature: %s, %s, %s" + "\n", + ((pevent-> + data[4] & DEV_FEATURE_BT) ? + "BT Feature" : "No BT Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BTAMP) ? + "BTAMP Feature" : "No BTAMP Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BLE) ? + "BLE Feature" : "No BLE Feature") + ); + } + if (pevent->data[1] == MODULE_SHUTDOWN_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2]) ? "Shut down Fail" + : "Shut down success"); + + } + if (pevent->data[2]) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + } else { + PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n"); + ret = BT_STATUS_FAILURE; + } + break; + case BT_EVENT_POWER_STATE: + if (pevent->data[1] == BT_PS_SLEEP) + priv->adapter->ps_state = PS_SLEEP; + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); + + break; + case BT_CMD_SDIO_PULL_CFG_REQ: + if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS) + PRINTM(CMD, "BT: %s: SDIO pull configuration success\n", + m_dev->name); + + else { + PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n", + m_dev->name); + + } + break; + default: + PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0], + m_dev->name); + ret = BT_STATUS_FAILURE; + break; + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** + * @brief This function save the dump info to file + * + * + * @param dir_name directory name + * @param file_name file_name + * @return 0 --success otherwise fail + */ +int +bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len) +{ + int ret = BT_STATUS_SUCCESS; + struct file *pfile = NULL; + u8 name[64]; + mm_segment_t fs; + loff_t pos; + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(ERROR, "Can't save dump info to file\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + sprintf((char *)name, "%s/%s", dir_name, file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + sprintf((char *)name, "%s/%s", "/var", file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(ERROR, "Create Dump file for %s error\n", name); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name); + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(pfile, (const char __user *)buf, buf_len, &pos); + filp_close(pfile, NULL); + set_fs(fs); + + PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name); + +done: + LEAVE(); + return ret; +} + +#define DEBUG_HOST_READY 0xEE +#define DEBUG_FW_DONE 0xFF +#define DUMP_MAX_POLL_TRIES 200 + +#define DEBUG_DUMP_CTRL_REG 0xF0 +#define DEBUG_DUMP_START_REG 0xF1 +#define DEBUG_DUMP_END_REG 0xF8 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_APU_REGS = 3, + DUMP_TYPE_CIU_REGS = 4, + DUMP_TYPE_ICU_REGS = 5, + DUMP_TYPE_MAC_REGS = 6, + DUMP_TYPE_EXTEND_7 = 7, + DUMP_TYPE_EXTEND_8 = 8, + DUMP_TYPE_EXTEND_9 = 9, + DUMP_TYPE_EXTEND_10 = 10, + DUMP_TYPE_EXTEND_11 = 11, + DUMP_TYPE_EXTEND_12 = 12, + DUMP_TYPE_EXTEND_13 = 13, + DUMP_TYPE_EXTEND_LAST = 14 +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 + +typedef struct { + u8 mem_name[MAX_NAME_LEN]; + u8 *mem_Ptr; + struct file *pfile_mem; + u8 done_flag; +} memory_type_mapping; + +memory_type_mapping bt_mem_type_mapping_tbl[] = { + {"ITCM", NULL, NULL, 0xF0}, + {"DTCM", NULL, NULL, 0xF1}, + {"SQRAM", NULL, NULL, 0xF2}, + {"APU", NULL, NULL, 0xF3}, + {"CIU", NULL, NULL, 0xF4}, + {"ICU", NULL, NULL, 0xF5}, + {"MAC", NULL, NULL, 0xF6}, + {"EXT7", NULL, NULL, 0xF7}, + {"EXT8", NULL, NULL, 0xF8}, + {"EXT9", NULL, NULL, 0xF9}, + {"EXT10", NULL, NULL, 0xFA}, + {"EXT11", NULL, NULL, 0xFB}, + {"EXT12", NULL, NULL, 0xFC}, + {"EXT13", NULL, NULL, 0xFD}, + {"EXTLAST", NULL, NULL, 0xFE}, +}; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +/** + * @brief This function read/write firmware via cmd52 + * + * @param phandle A pointer to moal_handle + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status +bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag) +{ + int ret = 0; + int tries = 0; + u8 ctrl_data = 0; + u8 dbg_dump_ctrl_reg = 0; + + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG; + + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) { + ctrl_data = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)-> + func, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != DEBUG_HOST_READY) { + PRINTM(INFO, + "The ctrl reg was changed, re-try again!\n"); + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev. + card)->func, DEBUG_HOST_READY, + dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } + udelay(100); + } + if (ctrl_data == DEBUG_HOST_READY) { + PRINTM(ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void +bt_dump_firmware_info_v2(bt_private *priv) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr = NULL; + u8 dump_num = 0; + u8 idx = 0; + u8 doneflag = 0; + rdwr_status stat; + u8 i = 0; + u8 read_reg = 0; + u32 memory_size = 0; + u8 path_name[64], file_name[32]; + u8 *end_ptr = NULL; + u8 dbg_dump_start_reg = 0; + u8 dbg_dump_end_reg = 0; + + if (!priv) { + PRINTM(ERROR, "Could not dump firmwware info\n"); + return; + } + + memset(path_name, 0, sizeof(path_name)); + strcpy((char *)path_name, "/data"); + PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name); + + dbg_dump_start_reg = DEBUG_DUMP_START_REG; + dbg_dump_end_reg = DEBUG_DUMP_END_REG; + + sbi_wakeup_firmware(priv); + sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + /* start dump fw memory */ + PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n"); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + reg = dbg_dump_start_reg; + dump_num = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ MEM NUM ERR\n"); + goto done; + } + + /* read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + if (RDWR_STATUS_FAILURE == + bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + memory_size = 0; + reg = dbg_dump_start_reg; + for (i = 0; i < 4; i++) { + read_reg = + sdio_readb(((struct sdio_mmc_card *)priv-> + bt_dev.card)->func, reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + if (memory_size == 0) { + PRINTM(MSG, "Firmware Dump Finished!\n"); + break; + } else { + PRINTM(MSG, "%s_SIZE=0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + memory_size); + bt_mem_type_mapping_tbl[idx].mem_Ptr = + vmalloc(memory_size + 1); + if ((ret != BT_STATUS_SUCCESS) || + !bt_mem_type_mapping_tbl[idx].mem_Ptr) { + PRINTM(ERROR, + "Error: vmalloc %s buffer failed!!!\n", + bt_mem_type_mapping_tbl[idx].mem_name); + goto done; + } + dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr; + end_ptr = dbg_ptr + memory_size; + } + doneflag = bt_mem_type_mapping_tbl[idx].done_flag; + PRINTM(MSG, "Start %s output, please wait...\n", + bt_mem_type_mapping_tbl[idx].mem_name); + do { + stat = bt_cmd52_rdwr_firmware(priv, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = + sdio_readb(((struct sdio_mmc_card *) + priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + PRINTM(MSG, + "pre-allocced buf is not enough\n"); + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MSG, "%s done:" + "size = 0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + (unsigned int)(dbg_ptr - + bt_mem_type_mapping_tbl + [idx].mem_Ptr)); + memset(file_name, 0, sizeof(file_name)); + sprintf((char *)file_name, "%s%s", "file_bt_", + bt_mem_type_mapping_tbl[idx].mem_name); + if (BT_STATUS_SUCCESS != + bt_save_dump_info_to_file((char *)path_name, + (char *)file_name, + bt_mem_type_mapping_tbl + [idx].mem_Ptr, + memory_size)) + PRINTM(MSG, + "Can't save dump file %s in %s\n", + file_name, path_name); + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + break; + } + } while (1); + } + PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n"); + /* end dump fw memory */ +done: + sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + for (idx = 0; idx < dump_num; idx++) { + if (bt_mem_type_mapping_tbl[idx].mem_Ptr) { + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + } + } + PRINTM(MSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function shows debug info for timeout of command sending. + * + * @param adapter A pointer to bt_private + * @param cmd Timeout command id + * + * @return N/A + */ +static void +bt_cmd_timeout_func(bt_private *priv, u16 cmd) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + + adapter->num_cmd_timeout++; + + PRINTM(ERROR, "Version = %s\n", adapter->drv_ver); + PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd); + PRINTM(ERROR, "Number of command timeout = %d\n", + adapter->num_cmd_timeout); + PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter); + PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode); + PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state); + PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state); + PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip); + PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail); + PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended); + PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries); + PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete); + PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv); + PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done); + PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending); + PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg); + bt_dump_sdio_regs(priv); + LEAVE(); +} + +/** + * @brief This function send reset cmd to firmware + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_reset_command(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET); + pcmd->length = 0x00; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, 3); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue Reset Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Reset timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_RESET); + } else { + PRINTM(CMD, "BT: Reset Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends module cfg cmd to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param subcmd sub command + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_module_cfg_cmd(bt_private *priv, int subcmd) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ); + pcmd->length = 1; + pcmd->data[0] = subcmd; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue module cfg Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + /* + On some Android platforms certain delay is needed for HCI daemon to + remove this module and close itself gracefully. Otherwise it hangs. + This 10ms delay is a workaround for such platforms as the root cause + has not been found yet. */ + mdelay(10); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n", + subcmd, priv->bt_dev.sendcmdflag); + bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ); + } else { + PRINTM(CMD, "BT: module cfg Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function to get histogram + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_get_histogram(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HISTOGRAM); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Histogram cmd(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + priv->hist_data_len = 0; + memset(priv->hist_data, 0, sizeof(priv->hist_data)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: histogram timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HISTOGRAM); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables power save mode + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_ps(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE); + if (priv->bt_dev.psmode) + pcmd->data[0] = BT_PS_ENABLE; + else + pcmd->data[0] = BT_PS_DISABLE; + if (priv->bt_dev.idle_timeout) { + pcmd->length = 3; + pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff); + pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8; + } else { + pcmd->length = 1; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: psmode timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends hscfg command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_hscfg_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG); + pcmd->length = 2; + pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8; + pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: HSCFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends sdio pull ctrl command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_sdio_pull_ctrl_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ); + pcmd->length = 4; + pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff); + pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8; + pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16; + pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, + "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0], + pcmd->data[3], pcmd->data[2]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends command to configure PMIC + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_pmic_configure(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: PMIC Configure timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables host sleep + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_hs(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + priv->adapter->suspend_fail = FALSE; + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + PRINTM(CMD, "Queue hs enable Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->hs_state, + WAIT_UNTIL_HS_STATE_CHANGED)) { + PRINTM(MSG, "BT: Enable host sleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE); + } + OS_INT_DISABLE; + if ((priv->adapter->hs_state == HS_ACTIVATED) || + (priv->adapter->is_suspended == TRUE)) { + OS_INT_RESTORE; + PRINTM(MSG, "BT: suspend success! skip=%d\n", + priv->adapter->hs_skip); + } else { + priv->adapter->suspend_fail = TRUE; + OS_INT_RESTORE; + priv->adapter->hs_skip++; + ret = BT_STATUS_FAILURE; + PRINTM(MSG, + "BT: suspend skipped! " + "state=%d skip=%d ps_state= %d WakeupTries=%d\n", + priv->adapter->hs_state, priv->adapter->hs_skip, + priv->adapter->ps_state, priv->adapter->WakeupTries); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Set Evt Filter + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_evt_filter(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER); + pcmd->length = 0x03; + pcmd->data[0] = 0x02; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set Evt Filter timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Write Scan - Page and Inquiry + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_write_scan(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN); + pcmd->length = 0x01; + pcmd->data[0] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Write Scan timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Device under test mode + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_device_under_testmode(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE); + pcmd->length = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables test mode and send cmd + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_test_mode(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + /** Set Evt Filter Command */ + ret = bt_set_evt_filter(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n"); + goto exit; + } + + /** Enable Write Scan Command */ + ret = bt_enable_write_scan(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n"); + goto exit; + } + + /** Enable Device under test mode */ + ret = bt_enable_device_under_testmode(priv); + if (ret != BT_STATUS_SUCCESS) + PRINTM(ERROR, + "BT test_mode: Enable device under testmode fail\n"); + +exit: + LEAVE(); + return ret; +} + +#define DISABLE_RESET 0x0 +#define ENABLE_OUTBAND_RESET 0x1 +#define ENABLE_INBAND_RESET 0x02 +#define DEFAULT_GPIO 0xff +/** + * @brief This function set GPIO pin + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_independent_reset(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + u8 mode, gpio; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET); + mode = btindrst & 0xff; + gpio = (btindrst & 0xff00) >> 8; + if (mode == ENABLE_OUTBAND_RESET) { + pcmd->data[0] = ENABLE_OUTBAND_RESET; + if (!gpio) + pcmd->data[1] = DEFAULT_GPIO; + else + pcmd->data[1] = gpio; + } else if (mode == ENABLE_INBAND_RESET) { + pcmd->data[0] = ENABLE_INBAND_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else if (mode == DISABLE_RESET) { + pcmd->data[0] = DISABLE_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else { + PRINTM(WARN, "Unsupport mode\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio); + pcmd->length = 2; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Independent reset : timeout!\n"); + bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets ble deepsleep mode + * + * @param priv A pointer to bt_private structure + * @param mode TRUE/FALSE + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_ble_deepsleep(bt_private *priv, int mode) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_BLE_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_BLE_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP); + pcmd->length = 1; + pcmd->deepsleep = mode; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_BLE_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode, + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function gets FW version + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_get_fw_version(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION); + pcmd->length = 0x01; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, 4); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Get FW version: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets mac address + * + * @param priv A pointer to bt_private structure + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_mac_address(bt_private *priv, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + int i = 0; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR); + pcmd->length = 8; + pcmd->cmd_type = MRVL_VENDOR_PKT; + pcmd->cmd_len = 6; + for (i = 0; i < 6; i++) + pcmd->data[i] = mac[5 - i]; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_HCI_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac), + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set mac addr: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + int i = 0; + /* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01 + 0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0xF0}; */ + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA); + pcmd->length = 0x20; + pcmd->data[0] = 0x00; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x00; + pcmd->data[3] = 0x1C; + /* swip cal-data byte */ + for (i = 4; i < 32; i++) + pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i)); + if (mac != NULL) { + pcmd->data[2] = 0x01; /* skip checksum */ + for (i = 24; i < 30; i++) + pcmd->data[i] = mac[29 - i]; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate EXT data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT); + pcmd->length = cfg_data_len; + + memcpy(pcmd->data, config_data, cfg_data_len); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function writes value to CSU registers + * + * @param priv A pointer to bt_private structure + * @param type reg type + * @param offset register address + * @param value register value to write + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CSU_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CSU_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG); + pcmd->length = 7; + pcmd->type = type; + pcmd->offset[0] = (offset & 0x000000ff); + pcmd->offset[1] = (offset & 0x0000ff00) >> 8; + pcmd->offset[2] = (offset & 0x00ff0000) >> 16; + pcmd->offset[3] = (offset & 0xff000000) >> 24; + pcmd->value[0] = (value & 0x00ff); + pcmd->value[1] = (value & 0xff00) >> 8; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_CSU_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n", + type, offset, value); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Set CSU reg timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function used to restore tx_queue + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_restore_tx_queue(bt_private *priv) +{ + struct sk_buff *skb = NULL; + while (!skb_queue_empty(&priv->adapter->pending_queue)) { + skb = skb_dequeue(&priv->adapter->pending_queue); + if (skb) + skb_queue_tail(&priv->adapter->tx_queue, skb); + } + wake_up_interruptible(&priv->MainThread.waitQ); +} + +/** + * @brief This function used to send command to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_prepare_command(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (priv->bt_dev.hscfgcmd) { + priv->bt_dev.hscfgcmd = 0; + ret = bt_send_hscfg_cmd(priv); + } + if (priv->bt_dev.pscmd) { + priv->bt_dev.pscmd = 0; + ret = bt_enable_ps(priv); + } + if (priv->bt_dev.sdio_pull_ctrl) { + priv->bt_dev.sdio_pull_ctrl = 0; + ret = bt_send_sdio_pull_ctrl_cmd(priv); + } + if (priv->bt_dev.hscmd) { + priv->bt_dev.hscmd = 0; + if (priv->bt_dev.hsmode) + ret = bt_enable_hs(priv); + else { + ret = sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + } + } + if (priv->bt_dev.test_mode) { + priv->bt_dev.test_mode = 0; + ret = bt_enable_test_mode(priv); + } + LEAVE(); + return ret; +} + +/** @brief This function processes a single packet + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to skb which includes TX packet + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +send_single_packet(bt_private *priv, struct sk_buff *skb) +{ + int ret; + int has_realloc = 0; + ENTER(); + if (!skb || !skb->data) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) { + PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, + BT_UPLD_SIZE); + LEAVE(); + return BT_STATUS_FAILURE; + } + if (skb_headroom(skb) < BT_HEADER_LEN) { + skb = skb_realloc_headroom(skb, BT_HEADER_LEN); + if (!skb) { + PRINTM(ERROR, "TX error: realloc_headroom failed %d\n", + BT_HEADER_LEN); + LEAVE(); + return BT_STATUS_FAILURE; + } + has_realloc = 1; + } + /* This is SDIO specific header length: byte[3][2][1], * type: byte[0] + (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ + skb_push(skb, BT_HEADER_LEN); + skb->data[0] = (skb->len & 0x0000ff); + skb->data[1] = (skb->len & 0x00ff00) >> 8; + skb->data[2] = (skb->len & 0xff0000) >> 16; + skb->data[3] = bt_cb(skb)->pkt_type; + if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT) + PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n", + __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len); + ret = sbi_host_to_card(priv, skb->data, skb->len); + if (has_realloc) + kfree_skb(skb); + LEAVE(); + return ret; +} + +static void +update_stat_byte_tx(bt_private *priv, struct sk_buff *skb) +{ + ((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat. + byte_tx += skb->len; +} + +static void +update_stat_err_tx(bt_private *priv, struct sk_buff *skb) +{ + ((struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer)->stat. + err_tx++; +} + +#ifdef CONFIG_OF +/** + * @brief This function read the initial parameter from device tress + * + * + * @return N/A + */ +static void +bt_init_from_dev_tree(void) +{ + struct device_node *dt_node = NULL; + struct property *prop; + u32 data; + const char *string_data; + + ENTER(); + + if (!dts_enable) { + PRINTM(CMD, "DTS is disabled!"); + return; + } + + dt_node = of_find_node_by_name(NULL, "sd8xxx-bt"); + if (!dt_node) { + LEAVE(); + return; + } + for_each_property_of_node(dt_node, prop) { +#ifdef DEBUG_LEVEL1 + if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(CMD, "mbt_drvdbg=0x%x\n", data); + mbt_drvdbg = data; + } + } +#endif + else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + init_cfg = (char *)string_data; + PRINTM(CMD, "init_cfg=%s\n", init_cfg); + } + } else if (!strncmp + (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg_ext = (char *)string_data; + PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext); + } + } else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg = (char *)string_data; + PRINTM(CMD, "cal_cfg=%s\n", cal_cfg); + } + } else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + bt_mac = (char *)string_data; + PRINTM(CMD, "bt_mac=%s\n", bt_mac); + } + } else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btindrst = data; + PRINTM(CMD, "btindrst=%d\n", btindrst); + } + } else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btpmic = data; + PRINTM(CMD, "btpmic=%d\n", btpmic); + } + } + } + LEAVE(); + return; +} +#endif + +/** + * @brief This function initializes the adapter structure + * and set default value to the member of adapter. + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +static void +bt_init_adapter(bt_private *priv) +{ + ENTER(); +#ifdef CONFIG_OF + bt_init_from_dev_tree(); +#endif + skb_queue_head_init(&priv->adapter->tx_queue); + skb_queue_head_init(&priv->adapter->pending_queue); + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + init_waitqueue_head(&priv->adapter->cmd_wait_q); + LEAVE(); +} + +/** + * @brief This function initializes firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (fw == 0) { + sd_enable_host_int(priv); + goto done; + } + sd_disable_host_int(priv); + if (sbi_download_fw(priv)) { + PRINTM(ERROR, " FW failed to be download!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +#define FW_POLL_TRIES 100 +#define FW_RESET_REG 0x0EE +#define FW_RESET_VAL 0x99 + +/** + * @brief This function reload firmware + * + * @param priv A pointer to bt_private + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +static int +bt_reload_fw(bt_private *priv, int mode) +{ + int ret = 0, tries = 0; + u8 value = 1; + u32 reset_reg = FW_RESET_REG; + u8 reset_val = FW_RESET_VAL; + + ENTER(); + if ((mode != FW_RELOAD_SDIO_INBAND_RESET) && + (mode != FW_RELOAD_NO_EMULATION)) { + PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode); + return -EFAULT; + } + + /** flush pending tx_queue */ + skb_queue_purge(&priv->adapter->tx_queue); + if (mode == FW_RELOAD_SDIO_INBAND_RESET) { + sd_disable_host_int(priv); + /** Wake up firmware firstly */ + sbi_wakeup_firmware(priv); + + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = sd_write_reg(priv, reset_reg, 0xba); + if (!ret) { + ret = sd_read_reg(priv, reset_reg, &value); + if (!ret && (value == 0xba)) { + PRINTM(MSG, "Fw wake up\n"); + break; + } + } + udelay(1000); + } + + ret = sd_write_reg(priv, reset_reg, reset_val); + if (ret) { + PRINTM(ERROR, "Failed to write register.\n"); + goto done; + } + + /** Poll register around 1 ms */ + for (; tries < FW_POLL_TRIES; ++tries) { + ret = sd_read_reg(priv, reset_reg, &value); + if (ret) { + PRINTM(ERROR, "Failed to read register.\n"); + goto done; + } + if (value == 0) + /** FW is ready */ + break; + udelay(1000); + } + if (value) { + PRINTM(ERROR, + "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + } + + sd_enable_host_int(priv); + /** reload FW */ + ret = bt_init_fw(priv); + if (ret) { + PRINTM(ERROR, "Re download firmware failed.\n"); + ret = -EFAULT; + } + LEAVE(); + return ret; +done: + sd_enable_host_int(priv); + LEAVE(); + return ret; +} + +/** + * @brief This function request to reload firmware + * + * @param priv A pointer to bt_private + * @param mode fw reload mode. + * + * @return N/A + */ +void +bt_request_fw_reload(bt_private *priv, int mode) +{ + ENTER(); + if (mode == FW_RELOAD_WITH_EMULATION) { + bt_fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MSG, "BT: FW reload with re-emulation...\n"); + LEAVE(); + return; + } + /** Reload FW */ + priv->fw_reload = TRUE; + if (bt_reload_fw(priv, mode)) { + PRINTM(ERROR, "FW reload fail\n"); + goto done; + } + priv->fw_reload = FALSE; + /** Other operation here? */ +done: + LEAVE(); + return; +} + +/** + * @brief This function frees the structure of adapter + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_free_adapter(bt_private *priv) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + kfree(adapter->tx_buffer); + kfree(adapter->hw_regs_buf); + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; + + LEAVE(); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +/** + * @brief This function handles the BT ioctl + * + * @param hdev A pointer to hci_dev structure + * @cmd ioctl cmd + * @arg argument + * @return -ENOIOCTLCMD + */ +static int +bt_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) +{ + ENTER(); + LEAVE(); + return -ENOIOCTLCMD; +} +#endif + +/** + * @brief This function handles the wrapper_dev ioctl + * + * @param hev A pointer to wrapper_dev structure + * @cmd ioctl cmd + * @arg argument + * @return -ENOIOCTLCMD + */ +static int +mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg) +{ + bt_private *priv = NULL; + int ret = 0; +#ifdef BLE_WAKEUP + u16 len; +#endif + + ENTER(); + + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n"); + ret = -ENODEV; + goto done; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n", + m_dev->flags); + ret = -EBUSY; + goto done; + } + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { +#ifdef BLE_WAKEUP + case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM: + PRINTM(MSG, "BT: Set ble wakeup parameters\n"); + if (copy_from_user(&len, arg, sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params length\n"); + ret = -EFAULT; + goto done; + } + /** Convert little endian length */ + len = __le16_to_cpu(len); + if (len < 2) { + PRINTM(ERROR, + "BT_IOCTL: Invalid ble wakeup params len %d\n", + len); + ret = -EFAULT; + goto done; + } + if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + priv->ble_wakeup_buf = + kzalloc(len + sizeof(u16), GFP_KERNEL); + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t" + "for ble wakeup parameters\n"); + ret = -ENOMEM; + goto done; + } + priv->ble_wakeup_buf_size = len + sizeof(u16); + } + if (copy_from_user + (priv->ble_wakeup_buf, arg, len + sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf, + len + sizeof(u16)); + break; +#endif + default: + break; + } + +done: +#ifdef BLE_WAKEUP + if (ret && priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + LEAVE(); + return ret; +} + +/** + * @brief This function handles BT destruct + * + * @param hdev A pointer to hci_dev structure + * + * @return N/A + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) +static void +bt_destruct(struct hci_dev *hdev) +{ + ENTER(); + LEAVE(); + return; +} +#endif + +/** + * @brief This function handles wrapper device destruct + * + * @param m_dev A pointer to m_dev structure + * + * @return N/A + */ +static void +mdev_destruct(struct m_dev *m_dev) +{ + ENTER(); + LEAVE(); + return; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) +/** + * @brief This function handles the BT transmit + * + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +bt_send_frame(struct sk_buff *skb) +#else +/** + * @brief This function handles the BT transmit + * + * @param hdev A pointer to hci_dev structure + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +bt_send_frame(struct hci_dev *hdev, struct sk_buff *skb) +#endif +{ + bt_private *priv = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + struct hci_dev *hdev = (struct hci_dev *)skb->dev; +#else + skb->dev = (void *)hdev; +#endif + + ENTER(); + PRINTM(DATA, "bt_send_frame %s: Type=%d, len=%d\n", hdev->name, + bt_cb(skb)->pkt_type, skb->len); + DBG_HEXDUMP(CMD_D, "bt_send_frame", skb->data, skb->len); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (!hdev || !hci_get_drvdata(hdev)) { +#else + if (!hdev || !hdev->driver_data) { +#endif + PRINTM(ERROR, "Frame for unknown HCI device (hdev=NULL)\n"); + LEAVE(); + return -ENODEV; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + priv = (bt_private *)hci_get_drvdata(hdev); +#else + priv = (bt_private *)hdev->driver_data; +#endif + if (!test_bit(HCI_RUNNING, &hdev->flags)) { + PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n", + hdev->flags); + LEAVE(); + return -EBUSY; + } + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + } + if (priv->adapter->tx_lock == TRUE) + skb_queue_tail(&priv->adapter->pending_queue, skb); + else + skb_queue_tail(&priv->adapter->tx_queue, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handles the wrapper device transmit + * + * @param m_dev A pointer to m_dev structure + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb) +{ + bt_private *priv = NULL; + + ENTER(); + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n"); + LEAVE(); + return -ENODEV; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n", + m_dev->flags); + LEAVE(); + return -EBUSY; + } + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + m_dev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + m_dev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + m_dev->stat.sco_tx++; + break; + } + + if (priv->adapter->tx_lock == TRUE) + skb_queue_tail(&priv->adapter->pending_queue, skb); + else + skb_queue_tail(&priv->adapter->tx_queue, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function flushes the transmit queue + * + * @param hdev A pointer to hci_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +bt_flush(struct hci_dev *hdev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + bt_private *priv = (bt_private *)hci_get_drvdata(hdev); +#else + bt_private *priv = (bt_private *)hdev->driver_data; +#endif + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->pending_queue); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function flushes the transmit queue + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_flush(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->pending_queue); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function closes the bluetooth device + * + * @param hdev A pointer to hci_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +bt_close(struct hci_dev *hdev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + bt_private *priv = (bt_private *)hci_get_drvdata(hdev); +#else + bt_private *priv = (bt_private *)hdev->driver_data; +#endif + + ENTER(); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0) + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) { + LEAVE(); + return BT_STATUS_SUCCESS; + } +#endif + skb_queue_purge(&priv->adapter->tx_queue); + + module_put(THIS_MODULE); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function closes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_close(struct m_dev *m_dev) +{ + + ENTER(); + mdev_req_lock(m_dev); + if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + + if (m_dev->flush) + m_dev->flush(m_dev); + /* wait up pending read and unregister char dev */ + wake_up_interruptible(&m_dev->req_wait_q); + /* Drop queues */ + skb_queue_purge(&m_dev->rx_q); + if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + module_put(THIS_MODULE); + m_dev->flags = 0; + mdev_req_unlock(m_dev); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function opens the bluetooth device + * + * @param hdev A pointer to hci_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +static int +bt_open(struct hci_dev *hdev) +{ + ENTER(); + if (try_module_get(THIS_MODULE) == 0) + return BT_STATUS_FAILURE; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 3, 0) + set_bit(HCI_RUNNING, &hdev->flags); +#endif + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function opens the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +static int +mdev_open(struct m_dev *m_dev) +{ + ENTER(); + + if (try_module_get(THIS_MODULE) == 0) + return BT_STATUS_FAILURE; + + set_bit(HCI_RUNNING, &m_dev->flags); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function queries the wrapper device + * + * @param m_dev A pointer to m_dev structure + * @param arg arguement + * + * @return BT_STATUS_SUCCESS or other + */ +void +mdev_query(struct m_dev *m_dev, void *arg) +{ + struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer; + + ENTER(); + if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type))) + PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n"); + + LEAVE(); +} + +/** + * @brief This function initializes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +void +init_m_dev(struct m_dev *m_dev) +{ + m_dev->dev_pointer = NULL; + m_dev->driver_data = NULL; + m_dev->dev_type = 0; + m_dev->spec_type = 0; + skb_queue_head_init(&m_dev->rx_q); + init_waitqueue_head(&m_dev->req_wait_q); + init_waitqueue_head(&m_dev->rx_wait_q); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + init_MUTEX(&m_dev->req_lock); +#else + sema_init(&m_dev->req_lock, 1); +#endif + spin_lock_init(&m_dev->rxlock); + memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats)); + m_dev->open = mdev_open; + m_dev->close = mdev_close; + m_dev->flush = mdev_flush; + m_dev->send = mdev_send_frame; + m_dev->destruct = mdev_destruct; + m_dev->ioctl = mdev_ioctl; + m_dev->query = mdev_query; + m_dev->owner = THIS_MODULE; + +} + +/** + * @brief This function handles the major job in bluetooth driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to bt_thread structure + * @return BT_STATUS_SUCCESS + */ +static int +bt_service_main_thread(void *data) +{ + bt_thread *thread = data; + bt_private *priv = thread->priv; + bt_adapter *adapter = priv->adapter; + wait_queue_t wait; + struct sk_buff *skb; + ENTER(); + bt_activate_thread(thread); + init_waitqueue_entry(&wait, current); + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&thread->waitQ, &wait); + OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE); + if (priv->adapter->WakeupTries || + ((!priv->adapter->IntCounter) && + (!priv->bt_dev.tx_dnld_rdy || + skb_queue_empty(&priv->adapter->tx_queue)))) { + PRINTM(INFO, "Main: Thread sleeping...\n"); + schedule(); + } + OS_SET_THREAD_STATE(TASK_RUNNING); + remove_wait_queue(&thread->waitQ, &wait); + if (kthread_should_stop() || adapter->SurpriseRemoved) { + PRINTM(INFO, "main-thread: break from main thread: " + "SurpriseRemoved=0x%x\n", + adapter->SurpriseRemoved); + break; + } + + PRINTM(INFO, "Main: Thread waking up...\n"); + + if (priv->adapter->IntCounter) { + OS_INT_DISABLE; + adapter->IntCounter = 0; + OS_INT_RESTORE; + sbi_get_int_status(priv); + } else if ((priv->adapter->ps_state == PS_SLEEP) && + !skb_queue_empty(&priv->adapter->tx_queue)) { + priv->adapter->WakeupTries++; + sbi_wakeup_firmware(priv); + continue; + } + if (priv->adapter->ps_state == PS_SLEEP) + continue; + if (priv->bt_dev.tx_dnld_rdy == TRUE) { + if (!skb_queue_empty(&priv->adapter->tx_queue)) { + skb = skb_dequeue(&priv->adapter->tx_queue); + if (skb) { + if (send_single_packet(priv, skb)) + update_stat_err_tx(priv, skb); + else + update_stat_byte_tx(priv, skb); + kfree_skb(skb); + } + } + } + } + bt_deactivate_thread(thread); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param m_dev A pointer to m_dev structure + * @return N/A + */ +void +bt_interrupt(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + if (!priv || !priv->adapter) { + LEAVE(); + return; + } + PRINTM(INTR, "*\n"); + priv->adapter->ps_state = PS_AWAKE; + if (priv->adapter->hs_state == HS_ACTIVATED) { + PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name); + priv->adapter->hs_state = HS_DEACTIVATED; + } + priv->adapter->WakeupTries = 0; + priv->adapter->IntCounter++; + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); +} + +static void +bt_private_dynamic_release(struct kobject *kobj) +{ + bt_private *priv = container_of(kobj, bt_private, kobj); + ENTER(); + PRINTM(INFO, "free bt priv\n"); + kfree(priv); + LEAVE(); +} + +static struct kobj_type ktype_bt_private_dynamic = { + .release = bt_private_dynamic_release, +}; + +static bt_private * +bt_alloc_priv(void) +{ + bt_private *priv; + ENTER(); + priv = kzalloc(sizeof(bt_private), GFP_KERNEL); + if (priv) { + kobject_init(&priv->kobj, &ktype_bt_private_dynamic); + PRINTM(INFO, "alloc bt priv\n"); + } + LEAVE(); + return priv; +} + +struct kobject * +bt_priv_get(bt_private *priv) +{ + PRINTM(INFO, "bt priv get object"); + return kobject_get(&priv->kobj); +} + +void +bt_priv_put(bt_private *priv) +{ + PRINTM(INFO, "bt priv put object"); + kobject_put(&priv->kobj); +} + +/** + * @brief This function send init commands to firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_init_cmd(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + if (ret < 0) { + PRINTM(FATAL, "Module cfg command send failed!\n"); + goto done; + } + if (btindrst != -1) { + ret = bt_set_independent_reset(priv); + if (ret < 0) { + PRINTM(FATAL, "Independent reset failed!\n"); + goto done; + } + } + if (btpmic) { + if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) { + PRINTM(FATAL, "BT: PMIC Configure failed \n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + ret = bt_set_ble_deepsleep(priv, TRUE); + if (ret < 0) { + PRINTM(FATAL, "Enable BLE deepsleep failed!\n"); + goto done; + } + if (psmode) { + priv->bt_dev.psmode = TRUE; + priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME; + ret = bt_enable_ps(priv); + if (ret < 0) { + PRINTM(FATAL, "Enable PS mode failed!\n"); + goto done; + } + } +#if defined(SDIO_SUSPEND_RESUME) + priv->bt_dev.gpio_gap = DEF_GPIO_GAP; + ret = bt_send_hscfg_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "Send HSCFG failed!\n"); + goto done; + } +#endif + priv->bt_dev.sdio_pull_cfg = 0xffffffff; + priv->bt_dev.sdio_pull_ctrl = 0; + wake_up_interruptible(&priv->MainThread.waitQ); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reinit firmware after redownload firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_reinit_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + /* block all the packet from bluez */ + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) + priv->adapter->tx_lock = TRUE; + + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) { + priv->adapter->tx_lock = FALSE; + bt_restore_tx_queue(priv); + } + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); +done: + return ret; +} + +/** + * @brief Module configuration and register device + * + * @param priv A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_conf_dpc(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct hci_dev *hdev = NULL; + unsigned char dev_type = 0; + + ENTER(); + + priv->bt_dev.tx_dnld_rdy = TRUE; + + if (priv->fw_reload) { + bt_reinit_fw(priv); + LEAVE(); + return ret; + } + + if (drv_mode & DRV_MODE_BT) { + hdev = hci_alloc_dev(); + if (!hdev) { + PRINTM(FATAL, "Can not allocate HCI device\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + hdev->open = bt_open; + hdev->close = bt_close; + hdev->flush = bt_flush; + hdev->send = bt_send_frame; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + hdev->destruct = bt_destruct; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hdev->ioctl = bt_ioctl; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + hci_set_drvdata(hdev, priv); +#else + hdev->driver_data = priv; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) + hdev->owner = THIS_MODULE; +#endif + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE; + priv->bt_dev.m_dev[BT_SEQ].spec_type = BLUEZ_SPEC; + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)hdev; + priv->bt_dev.m_dev[BT_SEQ].driver_data = priv; + priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0; + } + + dev_type = HCI_SDIO; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + if (hdev) + hdev->bus = dev_type; +#else + if (hdev) + hdev->type = dev_type; +#endif /* >= 2.6.34 */ + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + + /** Process device tree init parameters before register hci device. + * Since uplayer device has not yet registered, no need to block tx queue. + * */ + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } else if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + /* Get FW version */ + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); + + if (hdev) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) + hdev->dev_type = priv->bt_dev.devType; +#endif + ret = hci_register_dev(hdev); + if (ret < 0) { + PRINTM(FATAL, "Can not register HCI device\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + snprintf((char *)priv->bt_dev.m_dev[BT_SEQ].name, + sizeof(priv->bt_dev.m_dev[BT_SEQ].name), hdev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ); + } + +done: + LEAVE(); + return ret; +err_kmalloc: + LEAVE(); + return ret; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the bt_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to bt_private structure + */ + +bt_private * +bt_add_card(void *card) +{ + bt_private *priv = NULL; + + ENTER(); + + priv = bt_alloc_priv(); + if (!priv) { + PRINTM(FATAL, "Can not allocate priv\n"); + LEAVE(); + return NULL; + } + + /* allocate buffer for bt_adapter */ + priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL); + if (!priv->adapter) { + PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n"); + goto err_kmalloc; + } + priv->adapter->tx_buffer = + kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->tx_buffer) { + PRINTM(FATAL, "Allocate buffer for transmit\n"); + goto err_kmalloc; + } + priv->adapter->tx_buf = + (u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT); + priv->adapter->hw_regs_buf = + kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->hw_regs_buf) { + PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n"); + goto err_kmalloc; + } + priv->adapter->hw_regs = + (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT); + bt_init_adapter(priv); + + PRINTM(INFO, "Starting kthread...\n"); + priv->MainThread.priv = priv; + spin_lock_init(&priv->driver_lock); + + bt_create_thread(bt_service_main_thread, &priv->MainThread, + "bt_main_service"); + + /* wait for mainthread to up */ + while (!priv->MainThread.pid) + os_sched_timeout(1); + + /** user config file */ + init_waitqueue_head(&priv->init_user_conf_wait_q); + + priv->bt_dev.card = card; + + ((struct sdio_mmc_card *)card)->priv = priv; + priv->adapter->sd_ireg = 0; + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (sbi_register_dev(priv) < 0) { + PRINTM(FATAL, "Failed to register bt device!\n"); + goto err_registerdev; + } + if (bt_init_fw(priv)) { + PRINTM(FATAL, "BT Firmware Init Failed\n"); + goto err_init_fw; + } + LEAVE(); + return priv; + +err_init_fw: + clean_up_m_devs(priv); + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); +err_registerdev: + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); +err_kmalloc: + if (priv->adapter) + bt_free_adapter(priv); + bt_priv_put(priv); + LEAVE(); + return NULL; +} + +/** + * @brief This function send hardware remove event + * + * @param priv A pointer to bt_private + * @return N/A + */ +void +bt_send_hw_remove_event(bt_private *priv) +{ + struct sk_buff *skb = NULL; + struct hci_dev *hdev = NULL; + ENTER(); + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC) + hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer; +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_HARDWARE_REMOVE 0x24 + skb = bt_skb_alloc(3, GFP_ATOMIC); + skb->data[0] = HCI_HARDWARE_ERROR_EVT; + skb->data[1] = 1; + skb->data[2] = HCI_HARDWARE_REMOVE; + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 3); + if (hdev) { + skb->dev = (void *)hdev; + PRINTM(MSG, "Send HW ERROR event\n"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + os_sched_timeout(5); + hdev->stat.byte_rx += 3; + } + LEAVE(); + return; +} + +#ifdef BLE_WAKEUP +/** + * @brief This function used to config BLE wakeup pattern + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +int +bt_config_ble_wakeup(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + u16 ocf, left_len; + u8 len, more_cmd; + u8 *pos; + + ENTER(); + + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT: no ble wakeup parameters found\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Config ble wakeup pattern\n"); + + pos = priv->ble_wakeup_buf; + left_len = *(u16 *) pos; + left_len = __le16_to_cpu(left_len); + pos += sizeof(u16); + + while (left_len >= 2) { + more_cmd = *pos; + len = *(pos + 1); + if (((len + 2) > left_len) || + (!more_cmd && ((len + 2) < left_len))) { + PRINTM(ERROR, "Invalid ble parameters\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy(skb_put(skb, len), pos + 2, len); + bt_cb(skb)->pkt_type = *(u8 *)skb->data; + skb_pull(skb, 1); + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data; + ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode); + priv->adapter->cmd_complete = FALSE; + + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, + "BT: Set Set ble wakeup cmd 0x%x timeout:\n", + priv->bt_dev.send_cmd_opcode); + bt_cmd_timeout_func(priv, ocf); + goto done; + } + + pos += len + 2; + left_len -= len + 2; + } + +done: + if (ret != BT_STATUS_SUCCESS) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function send system suspend event + * + * @param priv A pointer to bt_private + * @return BT_STATUS_SUCCESS + */ +int +bt_send_system_event(bt_private *priv, u8 flag) +{ + struct sk_buff *skb = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + + ENTER(); + + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return BT_STATUS_FAILURE; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + + skb = bt_skb_alloc(4, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "Fail to allocate sys suspend event skb\n"); + return BT_STATUS_FAILURE; + } + skb->data[0] = VENDOR_SPECIFIC_EVENT; + skb->data[1] = 2; + skb->data[2] = HCI_SYSTEM_SUSPEND_EVT; + if (flag) + skb->data[3] = HCI_SYSTEM_SUSPEND; + else + skb->data[3] = HCI_SYSTEM_RESUME; + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 4); + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + PRINTM(MSG, "Send system %s event\n", + flag ? "suspend" : "resume"); + if (!mdev_recv_frame(skb)) { +#define RX_WAIT_TIMEOUT 300 + mdev_bt->wait_rx_complete = TRUE; + mdev_bt->rx_complete_flag = FALSE; + if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q, + mdev_bt-> + rx_complete_flag, + RX_WAIT_TIMEOUT)) + PRINTM(MSG, "BT stack received the event\n"); + mdev_bt->stat.byte_rx += 4; + } + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * @return BT_STATUS_SUCCESS + */ +int +bt_remove_card(void *card) +{ + bt_private *priv = (bt_private *)card; + ENTER(); + if (!priv) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + if (!priv->adapter->SurpriseRemoved) { + if (BT_STATUS_SUCCESS == bt_send_reset_command(priv)) + bt_send_module_cfg_cmd(priv, MODULE_SHUTDOWN_REQ); + /* Disable interrupts on the card */ + sd_disable_host_int(priv); + priv->adapter->SurpriseRemoved = TRUE; + } + bt_send_hw_remove_event(priv); +#ifdef BLE_WAKEUP + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + wake_up_interruptible(&priv->adapter->cmd_wait_q); + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) { + os_sched_timeout(1); + wake_up_interruptible(&priv->MainThread.waitQ); + } + + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); + clean_up_m_devs(priv); + PRINTM(INFO, "Free Adapter\n"); + bt_free_adapter(priv); + bt_priv_put(priv); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function initializes module. + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_module(void) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + PRINTM(MSG, "BT: Loading driver\n"); + + bt_root_proc_init(); + + /** create char device class */ + chardev_class = class_create(THIS_MODULE, MODULE_NAME); + if (IS_ERR(chardev_class)) { + PRINTM(ERROR, "Unable to allocate class\n"); + bt_root_proc_remove(); + ret = PTR_ERR(chardev_class); + goto done; + } + + if (sbi_register() == NULL) { + bt_root_proc_remove(); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + if (ret) + PRINTM(MSG, "BT: Driver loading failed\n"); + else + PRINTM(MSG, "BT: Driver loaded successfully\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void +bt_exit_module(void) +{ + ENTER(); + PRINTM(MSG, "BT: Unloading driver\n"); + sbi_unregister(); + + bt_root_proc_remove(); + class_destroy(chardev_class); + PRINTM(MSG, "BT: Driver unloaded\n"); + LEAVE(); +} + +module_init(bt_init_module); +module_exit(bt_exit_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +module_param(fw, int, 0); +MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware"); +module_param(psmode, int, 0); +MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode"); +#ifdef CONFIG_OF +module_param(dts_enable, int, 0); +MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS"); +#endif +#ifdef DEBUG_LEVEL1 +module_param(mbt_drvdbg, uint, 0); +MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL"); +#endif +#ifdef SDIO_SUSPEND_RESUME +module_param(mbt_pm_keep_power, int, 0); +MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power"); +#endif +module_param(init_cfg, charp, 0); +MODULE_PARM_DESC(init_cfg, "BT init config file name"); +module_param(cal_cfg, charp, 0); +MODULE_PARM_DESC(cal_cfg, "BT calibrate file name"); +module_param(cal_cfg_ext, charp, 0); +MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name"); +module_param(bt_mac, charp, 0660); +MODULE_PARM_DESC(bt_mac, "BT init mac address"); +module_param(drv_mode, int, 0); +MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;"); +module_param(bt_fw_reload, int, 0); +MODULE_PARM_DESC(bt_fw_reload, + "0: disable fw_reload; 1: enable fw reload feature"); +module_param(btindrst, int, 0); +MODULE_PARM_DESC(btindrst, + "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset."); +module_param(btpmic, int, 0); +MODULE_PARM_DESC(btpmic, + "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)"); +module_param(bt_fw_serial, int, 0); +MODULE_PARM_DESC(bt_fw_serial, + "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8997/bt/bt_proc.c b/bt_sd8997/bt/bt_proc.c new file mode 100644 index 0000000..c401791 --- /dev/null +++ b/bt_sd8997/bt/bt_proc.c
@@ -0,0 +1,881 @@ +/** @file bt_proc.c + * + * @brief This file handle the functions for proc files + * + * Copyright (C) 2007-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/proc_fs.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** proc diretory root */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#define PROC_DIR NULL +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#define PROC_DIR (&proc_root) +#else +#define PROC_DIR proc_net +#endif + +/** Proc mbt directory entry */ +static struct proc_dir_entry *proc_mbt; + +#define CMD52_STR_LEN 50 +static char cmd52_string[CMD52_STR_LEN]; + +struct proc_data { + /** Read length */ + int rdlen; + /** Read buffer */ + char *rdbuf; + /** Write length */ + int wrlen; + /** Maximum write length */ + int maxwrlen; + /** Write buffer */ + char *wrbuf; + /** Private structure */ + struct _bt_private *pbt; + void (*on_close) (struct inode *, struct file *); +}; + +/** Default file permission */ +#define DEFAULT_FILE_PERM 0644 + +/** Bluetooth device offset */ +#define OFFSET_BT_DEV 0x01 +/** Bluetooth adapter offset */ +#define OFFSET_BT_ADAPTER 0x02 +/** Show integer */ +#define SHOW_INT 0x10 +/** Show hex */ +#define SHOW_HEX 0x20 +/** Show string */ +#define SHOW_STRING 0x40 + +/** Device size */ +#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n) +/** Device address */ +#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n) + +/** Adapter size */ +#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n) +/** Adapter address */ +#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n) + +static struct item_data config_items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX} + , +#endif + {"idle_timeout", item_dev_size(idle_timeout), 0, + item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX} + , + {"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap), + OFFSET_BT_DEV | SHOW_HEX} + , + {"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0, + item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX} + , + {"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0, + item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT} + , + {"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode), + OFFSET_BT_DEV | SHOW_INT} + , + +}; + +static struct item_data status_items[] = { + {"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver), + OFFSET_BT_ADAPTER | SHOW_STRING}, + {"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0, + item_dev_addr(tx_dnld_rdy), + OFFSET_BT_DEV | SHOW_INT}, + {"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_state", item_adapter_size(hs_state), 0, + item_adapter_addr(hs_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"ps_state", item_adapter_size(ps_state), 0, + item_adapter_addr(ps_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"WakeupTries", item_adapter_size(WakeupTries), 0, + item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_recv", item_adapter_size(irq_recv), 0, + item_adapter_addr(irq_recv), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_done", item_adapter_size(irq_done), 0, + item_adapter_addr(irq_done), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"skb_pending", item_adapter_size(skb_pending), 0, + item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT}, +}; + +static struct item_data debug_items[] = { + {"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING}, +}; + +/** + * @brief convert string to number + * + * @param s pointer to numbered string + * @return converted number from string s + */ +int +string_to_number(char *s) +{ + int r = 0; + int base = 0; + int pn = 1; + + if (strncmp(s, "-", 1) == 0) { + pn = -1; + s++; + } + if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) { + base = 16; + s += 2; + } else + base = 10; + + for (s = s; *s != 0; s++) { + if ((*s >= '0') && (*s <= '9')) + r = (r * base) + (*s - '0'); + else if ((*s >= 'A') && (*s <= 'F')) + r = (r * base) + (*s - 'A' + 10); + else if ((*s >= 'a') && (*s <= 'f')) + r = (r * base) + (*s - 'a' + 10); + else + break; + } + + return r * pn; +} + +/** + * @brief Create cmd52 string + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +static int +form_cmd52_string(bt_private *priv) +{ + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X", + priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg, + priv->bt_dev.cmd52_val); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/* + * @brief Parse cmd52 string + * + * @param buffer A pointer user buffer + * @param len Length of user buffer + * @param func Parsed func number + * @param reg Parsed reg value + * @param val Parsed value to set + * @return BT_STATUS_SUCCESS + */ +static int +parse_cmd52_string(const char __user * buffer, size_t len, + int *func, int *reg, int *val) +{ + int ret = BT_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + + ENTER(); + + string = kzalloc(CMD52_STR_LEN, GFP_KERNEL); + if (!string) { + PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n"); + LEAVE(); + return -ENOMEM; + } + memcpy(string, buffer + strlen("sdcmd52rw="), + len - strlen("sdcmd52rw=")); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) + *func = string_to_number(pos); + + /* Get reg */ + pos = strsep(&string, " \t"); + if (pos) + *reg = string_to_number(pos); + + /* Get val (optional) */ + pos = strsep(&string, " \t"); + if (pos) + *val = string_to_number(pos); + kfree(string); + LEAVE(); + return ret; +} + +/** + * @brief This function handle generic proc file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS + */ +static int +proc_close(struct inode *inode, struct file *file) +{ + struct proc_data *pdata = file->private_data; + ENTER(); + if (pdata) { + if (pdata->on_close != NULL) + pdata->on_close(inode, file); + kfree(pdata->rdbuf); + kfree(pdata->wrbuf); + kfree(pdata); + } + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handle generic proc file read + * + * @param file A pointer to file structure + * @param buffer A pointer to output buffer + * @param len number of byte to read + * @param offset A pointer to offset of file + * @return number of output data + */ +static ssize_t +proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + if ((!pdata->rdbuf) || (pos < 0)) + return -EINVAL; + if (pos >= pdata->rdlen) + return 0; + if (len > pdata->rdlen - pos) + len = pdata->rdlen - pos; + if (copy_to_user(buffer, pdata->rdbuf + pos, len)) + return -EFAULT; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle generic proc file write + * + * @param file A pointer to file structure + * @param buffer A pointer to input buffer + * @param len number of byte to write + * @param offset A pointer to offset of file + * @return number of input data + */ +static ssize_t +proc_write(struct file *file, + const char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + int func = 0, reg = 0, val = 0; + int config_data = 0; + char *line = NULL; + + if (!pdata->wrbuf || (pos < 0)) + return -EINVAL; + if (pos >= pdata->maxwrlen) + return 0; + if (len > pdata->maxwrlen - pos) + len = pdata->maxwrlen - pos; + if (copy_from_user(pdata->wrbuf + pos, buffer, len)) + return -EFAULT; + if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) { + if (!strncmp + (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) { + line = pdata->wrbuf + pos; + line += strlen("fw_reload") + 1; + config_data = string_to_number(line); + } else + config_data = FW_RELOAD_SDIO_INBAND_RESET; + PRINTM(MSG, "Request fw_reload=%d\n", config_data); + bt_request_fw_reload(pdata->pbt, config_data); + } + if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) { + parse_cmd52_string(pdata->wrbuf + pos, len, &func, ®, &val); + sd_write_cmd52_val(pdata->pbt, func, reg, val); + } + if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) { + bt_dump_sdio_regs(pdata->pbt); + bt_dump_firmware_info_v2(pdata->pbt); + } + + if (pos + len > pdata->wrlen) + pdata->wrlen = len + file->f_pos; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle the generic file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return N/A + */ +static void +proc_on_close(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata = file->private_data; + char *line; + int i; + ENTER(); + if (!pdata->wrlen) + return; + line = pdata->wrbuf; + while (line[0]) { + for (i = 0; i < priv->num_items; i++) { + if (!strncmp + (line, priv->pdata[i].name, + strlen(priv->pdata[i].name))) { + line += strlen(priv->pdata[i].name) + 1; + if (priv->pdata[i].size == 1) + *((u8 *)priv->pdata[i].addr) = + (u8)string_to_number(line); + else if (priv->pdata[i].size == 2) + *((u16 *) priv->pdata[i].addr) = + (u16) string_to_number(line); + else if (priv->pdata[i].size == 4) + *((u32 *)priv->pdata[i].addr) = + (u32)string_to_number(line); + } + } + while (line[0] && line[0] != '\n') + line++; + if (line[0]) + line++; + } + if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd + || priv->pbt->bt_dev.sdio_pull_ctrl + || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) { + bt_prepare_command(priv->pbt); + wake_up_interruptible(&priv->pbt->MainThread.waitQ); + } + LEAVE(); + return; +} + +/** + * @brief This function handle the generic file open + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata; + int i; + char *p; + u32 val = 0; + ENTER(); + priv->pbt->adapter->skb_pending = + skb_queue_len(&priv->pbt->adapter->tx_queue); + file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL); + if (file->private_data == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n"); + LEAVE(); + return -ENOMEM; + } + pdata = (struct proc_data *)file->private_data; + pdata->pbt = priv->pbt; + pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL); + if (pdata->rdbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n"); + kfree(file->private_data); + LEAVE(); + return -ENOMEM; + } + if (priv->fileflag == DEFAULT_FILE_PERM) { + pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL); + if (pdata->wrbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n"); + kfree(pdata->rdbuf); + kfree(file->private_data); + return -ENOMEM; + } + pdata->maxwrlen = priv->bufsize; + pdata->on_close = proc_on_close; + } + p = pdata->rdbuf; + for (i = 0; i < priv->num_items; i++) { + if (priv->pdata[i].size == 1) + val = *((u8 *)priv->pdata[i].addr); + else if (priv->pdata[i].size == 2) + val = *((u16 *) priv->pdata[i].addr); + else if (priv->pdata[i].size == 4) + val = *((u32 *)priv->pdata[i].addr); + if (priv->pdata[i].flag & SHOW_INT) + p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_HEX) + p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_STRING) { + if (!strncmp + (priv->pdata[i].name, "sdcmd52rw", + strlen("sdcmd52rw"))) { + sd_read_cmd52_val(priv->pbt); + form_cmd52_string(priv->pbt); + } + p += sprintf(p, "%s=%s\n", priv->pdata[i].name, + (char *)priv->pdata[i].addr); + } + } + pdata->rdlen = strlen(pdata->rdbuf); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +static const struct file_operations proc_read_ops = { + .read = proc_read, + .open = proc_open, + .release = proc_close +}; + +static const struct file_operations proc_rw_ops = { + .read = proc_read, + .write = proc_write, + .open = proc_open, + .release = proc_close +}; + +static struct proc_private_data proc_files[] = { + {"status", S_IRUGO, 1024, + sizeof(status_items) / sizeof(status_items[0]), + &status_items[0], NULL, &proc_read_ops} + , + {"config", DEFAULT_FILE_PERM, 512, + sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL, + &proc_rw_ops} + , + {"debug", DEFAULT_FILE_PERM, 512, + sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL, + &proc_rw_ops} + , +}; + +/** + * @brief Proc read function for histogram + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +bt_histogram_read(struct seq_file *sfp, void *data) +{ + bt_hist_proc_data *pdata = (bt_hist_proc_data *) sfp->private; + bt_private *priv = (bt_private *)pdata->pbt; + u8 ant_num; + int i, j; + + ENTER(); + if (!priv) { + LEAVE(); + return -EFAULT; + } + bt_get_histogram(priv); + ant_num = priv->hist_data_len / sizeof(bt_histogram_data); + seq_printf(sfp, "BT histogram:\n"); + seq_printf(sfp, "antenna: 0=2.4G antenna a, 1=2.4G antenna b\n\n"); + if (ant_num < 1) { + seq_printf(sfp, "no histogram data from FW\n"); + LEAVE(); + return 0; + } + for (i = 0; i < ant_num; i++) { + if (pdata->antenna != priv->hist_data[i].antenna) + continue; + seq_printf(sfp, "antenna %d\n", priv->hist_data[i].antenna); + switch (priv->hist_data[i].powerclass) { + case 2: + seq_printf(sfp, "Power class=1.5\n"); + break; + case 5: + seq_printf(sfp, "Power class=2\n"); + break; + case 6: + seq_printf(sfp, "Power class=1\n"); + break; + default: + seq_printf(sfp, "Power class=%d\n", + priv->hist_data[i].powerclass); + break; + } + for (j = 0; j < (MAX_BT_LINK + MAX_BLE_LINK); j++) { + switch (priv->hist_data[i].link[j].txrxrate) { + case BDR_RATE_1M: + seq_printf(sfp, + "BT link[%d]: TxPower=%d dBm, TxRx Rate=BDR(1 mbps), RSSI=%d dBm\n", + j + 1, + priv->hist_data[i].link[j].txpower, + priv->hist_data[i].link[j].rssi); + break; + case EDR_RATE_2_3M: + seq_printf(sfp, + "BT link[%d]: TxPower=%d dBm, TxRx Rate=EDR(2/3 mbps), RSSI=%d dBm\n", + j + 1, + priv->hist_data[i].link[j].txpower, + priv->hist_data[i].link[j].rssi); + break; + case BLE_RATE_1M: + seq_printf(sfp, + "BLE link[%d]: TxPower=%d dBm, TxRx Rate=BLE(1 mbps), RSSI=%d dBm\n", + j - MAX_BT_LINK + 0x80, + priv->hist_data[i].link[j].txpower, + priv->hist_data[i].link[j].rssi); + break; + default: + if (j < MAX_BT_LINK) + seq_printf(sfp, + "BT link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n", + j + 1, + priv->hist_data[i].link[j]. + txpower, + priv->hist_data[i].link[j]. + txrxrate, + priv->hist_data[i].link[j]. + rssi); + else + seq_printf(sfp, + "BLE link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n", + j - MAX_BT_LINK + 0x80, + priv->hist_data[i].link[j]. + txpower, + priv->hist_data[i].link[j]. + txrxrate, + priv->hist_data[i].link[j]. + rssi); + break; + } + } + seq_printf(sfp, "\n"); + } + LEAVE(); + return 0; +} + +static int +bt_histogram_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, bt_histogram_read, PDE_DATA(inode)); +#else + return single_open(file, bt_histogram_read, PDE(inode)->data); +#endif +} + +static const struct file_operations histogram_proc_fops = { + .owner = THIS_MODULE, + .open = bt_histogram_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * @brief This function initializes proc entry + * + * @param priv A pointer to bt_private structure + * @param m_dev A pointer to struct m_dev + * @param seq Sequence number + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq) +{ + int ret = BT_STATUS_SUCCESS; + struct proc_dir_entry *entry; + int i, j; + char hist_entry[50]; + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + if (proc_mbt) { + priv->dev_proc[seq].proc_entry = + proc_mkdir(m_dev->name, proc_mbt); + if (!priv->dev_proc[seq].proc_entry) { + PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->dev_proc[seq].hist_entry = + proc_mkdir("histogram", priv->dev_proc[seq].proc_entry); + if (!priv->dev_proc[seq].hist_entry) { + PRINTM(ERROR, "BT: Could not mkdir histogram!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + for (i = 0; i < MAX_ANTENNA_NUM; i++) { + priv->hist_proc[i].antenna = i; + priv->hist_proc[i].pbt = priv; + snprintf(hist_entry, sizeof(hist_entry), "bt-ant%d", i); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + entry = proc_create_data(hist_entry, 0644, + priv->dev_proc[seq].hist_entry, + &histogram_proc_fops, + &priv->hist_proc[i]); + if (entry == NULL) +#else + entry = create_proc_entry(hist_entry, 0644, + priv->dev_proc[seq]. + hist_entry); + if (entry) { + entry->data = &priv->hist_proc[i]; + entry->proc_fops = &histogram_proc_fops; + } else +#endif + { + PRINTM(MSG, + "Fail to create histogram proc %s\n", + hist_entry); + ret = BT_STATUS_FAILURE; + goto done; + } + } + priv->dev_proc[seq].pfiles = + kmalloc(sizeof(proc_files), GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles) { + PRINTM(ERROR, + "BT: Could not alloc memory for pfile!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files, + sizeof(proc_files)); + priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files); + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) + priv->dev_proc[seq].pfiles[j].pdata = NULL; + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + priv->dev_proc[seq].pfiles[j].pdata = + kmalloc(priv->dev_proc[seq].pfiles[j]. + num_items * sizeof(struct item_data), + GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles[j].pdata) { + PRINTM(ERROR, + "BT: Could not alloc memory for pdata!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata, + (u8 *)proc_files[j].pdata, + priv->dev_proc[seq].pfiles[j].num_items * + sizeof(struct item_data)); + for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items; + i++) { + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_DEV) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)&priv->bt_dev; + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_ADAPTER) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)priv->adapter; + } + priv->dev_proc[seq].pfiles[j].pbt = priv; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + entry = proc_create_data(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq].proc_entry, + proc_files[j].fops, + &priv->dev_proc[seq]. + pfiles[j]); + if (entry == NULL) +#else + entry = create_proc_entry(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq]. + proc_entry); + if (entry) { + entry->data = &priv->dev_proc[seq].pfiles[j]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + entry->owner = THIS_MODULE; +#endif + entry->proc_fops = proc_files[j].fops; + } else +#endif + PRINTM(MSG, "BT: Fail to create proc %s\n", + proc_files[j].name); + } + } +done: + if (ret == BT_STATUS_FAILURE) { + if (priv->dev_proc[seq].proc_entry) { + remove_proc_entry(m_dev->name, proc_mbt); + priv->dev_proc[seq].proc_entry = NULL; + } + if (priv->dev_proc[seq].pfiles) { + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + if (priv->dev_proc[seq].pfiles[j].pdata) { + kfree(priv->dev_proc[seq].pfiles[j]. + pdata); + priv->dev_proc[seq].pfiles[j].pdata = + NULL; + } + } + kfree(priv->dev_proc[seq].pfiles); + priv->dev_proc[seq].pfiles = NULL; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function removes proc interface + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_proc_remove(bt_private *priv) +{ + int j, i; + char hist_entry[50]; + ENTER(); + PRINTM(INFO, "BT: Remove Proc Interface\n"); + if (proc_mbt) { + for (i = 0; i < MAX_RADIO_FUNC; i++) { + if (!priv->dev_proc[i].proc_entry) + continue; + for (j = 0; j < ARRAY_SIZE(proc_files); j++) { + remove_proc_entry(proc_files[j].name, + priv->dev_proc[i].proc_entry); + } + for (j = 0; j < MAX_ANTENNA_NUM; j++) { + snprintf(hist_entry, sizeof(hist_entry), + "bt-ant%d", j); + remove_proc_entry(hist_entry, + priv->dev_proc[i].hist_entry); + } + remove_proc_entry("histogram", + priv->dev_proc[i].proc_entry); + remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt); + priv->dev_proc[i].proc_entry = NULL; + + if (priv->dev_proc[i].pfiles) { + for (j = 0; + j < priv->dev_proc[i].num_proc_files; + j++) { + if (priv->dev_proc[i].pfiles[j].pdata) { + kfree(priv->dev_proc[i]. + pfiles[j].pdata); + priv->dev_proc[i].pfiles[j]. + pdata = NULL; + } + } + kfree(priv->dev_proc[i].pfiles); + priv->dev_proc[i].pfiles = NULL; + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function creates proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_root_proc_init(void) +{ + PRINTM(INFO, "BT: Create Proc Interface\n"); + proc_mbt = proc_mkdir("mbt", PROC_DIR); + if (!proc_mbt) { + PRINTM(ERROR, "BT: Cannot create proc interface\n"); + return BT_STATUS_FAILURE; + } + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function removes proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS + */ +int +bt_root_proc_remove(void) +{ + remove_proc_entry("mbt", PROC_DIR); + proc_mbt = NULL; + return BT_STATUS_SUCCESS; +}
diff --git a/bt_sd8997/bt/bt_sdio.h b/bt_sd8997/bt/bt_sdio.h new file mode 100644 index 0000000..b344e92 --- /dev/null +++ b/bt_sd8997/bt/bt_sdio.h
@@ -0,0 +1,258 @@ +/** @file bt_sdio.h + * @brief This file contains SDIO (interface) module + * related macros, enum, and structure. + * + * Copyright (C) 2007-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_SDIO_H_ +#define _BT_SDIO_H_ + +#include <linux/irqreturn.h> + +/** IRQ return type */ +typedef irqreturn_t IRQ_RET_TYPE; +/** IRQ return */ +#define IRQ_RET (return IRQ_HANDLED) +/** ISR notifier function */ +typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id, + struct pt_regs * reg); + +/** SDIO header length */ +#define SDIO_HEADER_LEN 4 + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */ +#define GPIO_INT_NEW_MODE 255 +/* SD block size can not bigger than 64 due to buf size limit in firmware */ +/** define SD block size for data Tx/Rx */ +#define SD_BLOCK_SIZE 64 +/** define SD block size for firmware download */ +#define SD_BLOCK_SIZE_FW_DL 256 + +/** Number of blocks for firmware transfer */ +#define FIRMWARE_TRANSFER_NBLOCK 2 + +/** Firmware ready */ +#define FIRMWARE_READY 0xfedc + +/* Bus Interface Control Reg 0x07 */ +/** SD BUS width 1 */ +#define SD_BUS_WIDTH_1 0x00 +/** SD BUS width 4 */ +#define SD_BUS_WIDTH_4 0x02 +/** SD BUS width mask */ +#define SD_BUS_WIDTH_MASK 0x03 +/** Asynchronous interrupt mode */ +#define ASYNC_INT_MODE 0x20 + +/** magic register */ +#define CARD_MAGIC_REG 0xF0 +/** magic value */ +#define MAGIC_VAL 0x24 + +/* Host Control Registers */ +/** Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/** Host Control Registers : Host without Command 53 finish host*/ +#define HOST_TO_CARD_EVENT (0x1U << 3) +/** Host Control Registers : Host terminates Command 53 */ +#define HOST_TERM_CMD53 (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Host interrupt RSR */ +#define HOST_INT_RSR_REG 0x04 + +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x08 + +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +/** Host Control Registers : Host interrupt status */ +#define HOST_INTSTATUS_REG 0x0C +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Host Control Registers : Host Transfer status */ +#define HOST_INT_STATUS_REG 0x58 +/** Host Control Registers : Upload CRC error */ +#define UP_LD_CRC_ERR (0x1U << 2) +/** Host Control Registers : Upload restart */ +#define UP_LD_RESTART (0x1U << 1) +/** Host Control Registers : Download restart */ +#define DN_LD_RESTART (0x1U << 0) + +/** Card Control Registers : Card to Host Event register */ +#define CARD_STATUS_REG 0x5C +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host interrupt mask register */ +#define HOST_INTERRUPT_MASK_REG 0x60 +/** Card Control Registers : Host power interrupt mask */ +#define HOST_POWER_INT_MASK (0x1U << 3) +/** Card Control Registers : Abort card interrupt mask */ +#define ABORT_CARD_INT_MASK (0x1U << 2) +/** Card Control Registers : Upload card interrupt mask */ +#define UP_LD_CARD_INT_MASK (0x1U << 1) +/** Card Control Registers : Download card interrupt mask */ +#define DN_LD_CARD_INT_MASK (0x1U << 0) + +/** Card Control Registers : Card interrupt status register */ +#define CARD_INTERRUPT_STATUS_REG 0x64 +/** Card Control Registers : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** Card Control Registers : Card interrupt RSR register */ +#define CARD_INTERRUPT_RSR_REG 0x68 +/** Card Control Registers : Power up RSR */ +#define POWER_UP_RSR (0x1U << 4) +/** Card Control Registers : Power down RSR */ +#define POWER_DOWN_RSR (0x1U << 3) + +/* Card Control Registers */ +/** Card Control Registers : Read SQ base address A0 register */ +#define SQ_READ_BASE_ADDRESS_A0_REG 0xf8 +/** Card Control Registers : Read SQ base address A1 register */ +#define SQ_READ_BASE_ADDRESS_A1_REG 0xf9 +/** Card Control Registers : Read SQ base address A2 register */ +#define SQ_READ_BASE_ADDRESS_A2_REG 0x6E +/** Card Control Registers : Read SQ base address A3 register */ +#define SQ_READ_BASE_ADDRESS_A3_REG 0x6F +/** Card Control Registers : Write SQ base address A0 register */ +#define SQ_WRITE_BASE_ADDRESS_A0_REG 0x70 +/** Card Control Registers : Write SQ base address A1 register */ +#define SQ_WRITE_BASE_ADDRESS_A1_REG 0x71 +/** Card Control Registers : Write SQ base address A2 register */ +#define SQ_WRITE_BASE_ADDRESS_A2_REG 0x72 +/** Card Control Registers : Write SQ base address A3 register */ +#define SQ_WRITE_BASE_ADDRESS_A3_REG 0x73 + +/** Card Control Registers : Card revision register */ +#define CARD_REVISION_REG 0xC8 + +/** Firmware status 0 register (SCRATCH0_0) */ +#define CARD_FW_STATUS0_REG 0xe8 +/** Firmware status 1 register (SCRATCH0_1) */ +#define CARD_FW_STATUS1_REG 0xe9 +/** Rx length register (SCRATCH0_2) */ +#define CARD_RX_LEN_REG 0xea +/** Rx unit register (SCRATCH0_3) */ +#define CARD_RX_UNIT_REG 0xeb +/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/ +#define ENABLE_GPIO_1_INT_MODE 0x88 +/** Scratch reg 3 2 : Configure GPIO-1 INT*/ +#define SCRATCH_REG_32 0xEE + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0xD4 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0xD5 +/** Card Control Registers : Card OCR 3 register */ +#define CARD_OCR_3_REG 0xD6 +/** Card Control Registers : Card config register */ +#define CARD_CONFIG_REG 0xD7 +/** Card Control Registers : Miscellaneous Configuration Register */ +#define CARD_MISC_CFG_REG 0xD8 +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT (0x1U << 4) + +/** Card Control Registers : Debug 0 register */ +#define DEBUG_0_REG 0xDC +/** Card Control Registers : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : Debug 1 register */ +#define DEBUG_1_REG 0xDD +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : Debug 2 register */ +#define DEBUG_2_REG 0xDE +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : Debug 3 register */ +#define DEBUG_3_REG 0xDF +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0xE4 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0xE5 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0xE6 + +struct sdio_mmc_card { + /** sdio_func structure pointer */ + struct sdio_func *func; + /** bt_private structure pointer */ + bt_private *priv; +}; + +/** DMA alignment value */ +#define DMA_ALIGNMENT 64 +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** This function read cmd52 register */ +int sd_write_reg(bt_private *priv, int reg, u8 val); +/** This function write cmd52 value to register */ +int sd_read_reg(bt_private *priv, int reg, u8 *data); +/** This function reads the Cmd52 value in dev structure */ +int sd_read_cmd52_val(bt_private *priv); +/** This function updates card reg based on the Cmd52 value in dev structure */ +int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val); + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** This function tells lower driver that BT is suspended */ +void bt_is_suspended(bt_private *priv); +#endif +#endif +#endif +#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8997/bt/bt_sdiommc.c b/bt_sd8997/bt/bt_sdiommc.c new file mode 100644 index 0000000..2bc4cdc --- /dev/null +++ b/bt_sd8997/bt/bt_sdiommc.c
@@ -0,0 +1,1935 @@ +/** @file bt_sdiommc.c + * @brief This file contains SDIO IF (interface) module + * related functions. + * + * Copyright (C) 2007-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/firmware.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/card.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** define marvell vendor id */ +#define MARVELL_VENDOR_ID 0x02df + +/** Max retry number of CMD53 read/write */ +#define MAX_CMD53_RETRY 3 +/** Max retry number of CMD53 read/write */ +#define MAX_CMD52_RETRY 3 +/** Firmware name */ +static char *fw_name; +/** fw serial download flag */ +extern int bt_fw_serial; +/** request firmware nowait */ +int bt_req_fw_nowait; +static int multi_fn = BIT(2); + +#define SD8997_FW_NAME "mrvl/sdsd8997_combo_v4.bin" +#define SD8997_BT_FW_NAME "mrvl/sd8997_bt_v4.bin" +#define DEFAULT_FW_NAME "mrvl/sdsd8997_combo_v4.bin" +#define DEFAULT_BT_FW_NAME "mrvl/sd8997_bt_v4.bin" + +/** Function number 2 */ +#define FN2 2 +/** Device ID for SD8997 FN2 */ +#define SD_DEVICE_ID_8997_BT_FN2 0x9142 + +/** Array of SDIO device ids when multi_fn=0x12 */ +static const struct sdio_device_id bt_ids[] = { + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)}, + {} +}; + +MODULE_DEVICE_TABLE(sdio, bt_ids); + +/******************************************************** + Global Variables +********************************************************/ +/** unregiser bus driver flag */ +static u8 unregister; +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +extern int mbt_pm_keep_power; +#endif + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function gets rx_unit value + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_get_rx_unit(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_unit_reg = CARD_RX_UNIT_REG; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_unit_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + priv->bt_dev.rx_unit = reg; + + LEAVE(); + return ret; +} + +/** + * @brief This function reads fwstatus registers + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_read_firmware_status(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 fws0; + u8 fws1; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = CARD_FW_STATUS0_REG; + u8 card_fw_status1_reg = CARD_FW_STATUS1_REG; + + ENTER(); + + fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + *dat = (((u16) fws1) << 8) | fws0; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function reads rx length + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sd_read_rx_len(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_len_reg = CARD_RX_LEN_REG; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_len_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + *dat = (u16) reg << priv->bt_dev.rx_unit; + + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts mask + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_enable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = HOST_INT_MASK_REG; + + ENTER(); + + sdio_writeb(card->func, mask, host_int_mask_reg, &ret); + if (ret) { + PRINTM(WARN, "BT: Unable to enable the host interrupt!\n"); + ret = BT_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** @brief This function disables the host interrupts mask. + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sd_disable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_FAILURE; + u8 host_int_mask; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = HOST_INT_MASK_REG; + + ENTER(); + + /* Read back the host_int_mask register */ + host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret); + if (ret) + goto done; + + /* Update with the mask and write back to the register */ + host_int_mask &= ~mask; + sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret); + if (ret < 0) { + PRINTM(WARN, "BT: Unable to diable the host interrupt!\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function polls the card status register + * + * @param priv A pointer to bt_private structure + * @param bits the bit mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_poll_card_status(bt_private *priv, u8 bits) +{ + int tries; + int rval; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 cs; + u8 card_status_reg = CARD_STATUS_REG; + + ENTER(); + + for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { + cs = sdio_readb(card->func, card_status_reg, &rval); + if (rval != 0) + break; + if (rval == 0 && (cs & bits) == bits) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + udelay(1); + } + PRINTM(ERROR, + "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n", + rval, tries, cs); + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_cmd52_val(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 func, reg, val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + func = priv->bt_dev.cmd52_func; + reg = priv->bt_dev.cmd52_reg; + sdio_claim_host(card->func); + if (func) + val = sdio_readb(card->func, reg, &ret); + else + val = sdio_f0_readb(card->func, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n", + func, reg); + } else { + priv->bt_dev.cmd52_val = val; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param func Stores func variable + * @param reg Stores reg variable + * @param val Stores val variable + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_cmd52_val(bt_private *priv, int func, int reg, int val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + if (val >= 0) { + /* Perform actual write only if val is provided */ + sdio_claim_host(card->func); + if (func) + sdio_writeb(card->func, val, reg, &ret); + else + sdio_f0_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, + "BT: Cannot write value (0x%x) to func %d reg %d\n", + val, func, reg); + goto done; + } + priv->bt_dev.cmd52_val = val; + } + + /* Save current func and reg for future read */ + priv->bt_dev.cmd52_func = func; + priv->bt_dev.cmd52_reg = reg; + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to write + * @param val value + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_reg(bt_private *priv, int reg, u8 val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + sdio_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to read + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_reg(bt_private *priv, int reg, u8 *data) +{ + int ret = BT_STATUS_SUCCESS; + u8 val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + *data = val; + LEAVE(); + return ret; +} + +/** + * @brief This function probes the card + * + * @param func A pointer to sdio_func structure. + * @param id A pointer to structure sdio_device_id + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = NULL; + struct sdio_mmc_card *card = NULL; + + ENTER(); + + PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor, + id->device, id->class, func->num); + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto done; + } + card->func = func; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + /* wait for chip fully wake up */ + if (!func->enable_timeout) + func->enable_timeout = 200; +#endif + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) { + sdio_disable_func(func); + sdio_release_host(func); + PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret); + kfree(card); + LEAVE(); + return -EIO; + } + sdio_release_host(func); + priv = bt_add_card(card); + if (!priv) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + ret = BT_STATUS_FAILURE; + kfree(card); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks if the firmware is ready to accept + * command or not. + * + * @param priv A pointer to bt_private structure + * @param pollnum Number of times to poll fw status + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_verify_fw_download(bt_private *priv, int pollnum) +{ + int ret = BT_STATUS_FAILURE; + u16 firmwarestat = 0; + int tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if (sd_read_firmware_status(priv, &firmwarestat) < 0) + continue; + if (firmwarestat == FIRMWARE_READY) { + PRINTM(MSG, "BT FW is active(%d)\n", tries); + ret = BT_STATUS_SUCCESS; + break; + } + mdelay(100); + } + if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) { + PRINTM(ERROR, + "Fail to poll firmware status: firmwarestat=0x%x\n", + firmwarestat); + bt_dump_sdio_regs(priv); + } + LEAVE(); + return ret; +} + +/** + * @brief Transfers firmware to card + * + * @param priv A Pointer to bt_private structure + * @param fw A Pointer to fw image + * @param fw_len fw image len + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len) +{ + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 *firmware = fw; + int firmwarelen = fw_len; + u8 base0; + u8 base1; + int ret = BT_STATUS_SUCCESS; + int offset; + void *tmpfwbuf = NULL; + int tmpfwbufsz; + u8 *fwbuf; + u16 len; + int txlen = 0; + int tx_blocks = 0; + int i = 0; + int tries = 0; + u8 sq_read_base_address_a0_reg = SQ_READ_BASE_ADDRESS_A0_REG; + u8 sq_read_base_address_a1_reg = SQ_READ_BASE_ADDRESS_A1_REG; + + ENTER(); + + PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen); + + tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT; + tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); + if (!tmpfwbuf) { + PRINTM(ERROR, + "BT: Unable to allocate buffer for firmware. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + /* Ensure aligned firmware buffer */ + fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); + + /* Perform firmware data transfer */ + offset = 0; + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits + */ + ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret < 0) { + PRINTM(FATAL, + "BT: FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + /* More data? */ + if (offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + base0 = sdio_readb(card->func, + sq_read_base_address_a0_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", + base0, base0); + ret = BT_STATUS_FAILURE; + goto done; + } + base1 = sdio_readb(card->func, + sq_read_base_address_a1_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download\n", + base1, base1); + ret = BT_STATUS_FAILURE; + goto done; + } + len = (((u16) base1) << 8) | base0; + + if (len != 0) + break; + udelay(10); + } + + if (len == 0) + break; + else if (len > BT_UPLD_SIZE) { + PRINTM(FATAL, + "BT: FW download failure @ %d, invalid length %d\n", + offset, len); + ret = BT_STATUS_FAILURE; + goto done; + } + /** ignore CRC check before download the first packet */ + if (offset == 0 && (len & BIT(0))) + len &= ~BIT(0); + txlen = len; + + if (len & BIT(0)) { + i++; + if (i >= MAX_CMD53_RETRY) { + PRINTM(FATAL, + "BT: FW download failure @ %d, over max retry count\n", + offset); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: FW CRC error indicated by the helper:" + " len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + + PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last block */ + if (firmwarelen - offset < txlen) + txlen = firmwarelen - offset; + + PRINTM(INFO, "."); + + tx_blocks = + (txlen + SD_BLOCK_SIZE_FW_DL - + 1) / SD_BLOCK_SIZE_FW_DL; + + /* Copy payload to buffer */ + memcpy(fwbuf, &firmware[offset], txlen); + } + + /* Send data */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf, + tx_blocks * SD_BLOCK_SIZE_FW_DL); + + if (ret < 0) { + PRINTM(ERROR, + "BT: FW download, write iomem (%d) failed @ %d\n", + i, offset); + sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret); + if (ret) + PRINTM(ERROR, "write ioreg failed (CFG)\n"); + } + + offset += txlen; + } while (TRUE); + + PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset); + + ret = BT_STATUS_SUCCESS; +done: + kfree(tmpfwbuf); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * + * @param fw_firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_request_fw_dpc(const struct firmware *fw_firmware, void *context) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = (bt_private *)context; + struct sdio_mmc_card *card = NULL; + struct m_dev *m_dev_bt = NULL; + struct timeval tstamp; + + ENTER(); + + m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ]; + + if ((priv == NULL) || (priv->adapter == NULL) || + (priv->bt_dev.card == NULL) || (m_dev_bt == NULL) + ) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + card = (struct sdio_mmc_card *)priv->bt_dev.card; + + if (!fw_firmware) { + do_gettimeofday(&tstamp); + if (tstamp.tv_sec > + (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(ERROR, + "BT: No firmware image found. Skipping download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: No firmware image found! Retrying download\n"); + /* Wait a second here before calling the callback again */ + os_sched_timeout(1000); + sd_download_firmware_w_helper(priv); + LEAVE(); + return ret; + } + + priv->firmware = fw_firmware; + + if (BT_STATUS_FAILURE == + sd_init_fw_dpc(priv, (u8 *)priv->firmware->data, + priv->firmware->size)) { + PRINTM(ERROR, + "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n", + bt_req_fw_nowait); + sdio_release_host(card->func); + ret = BT_STATUS_FAILURE; + goto done; + } + + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) { + PRINTM(ERROR, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + sdio_release_host(card->func); + goto done; + } + sdio_release_host(card->func); + sd_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + LEAVE(); + return ret; + +done: + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + /* For synchronous download cleanup will be done in add_card */ + if (!bt_req_fw_nowait) + return ret; + PRINTM(INFO, "unregister device\n"); + sbi_unregister_dev(priv); + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); + bt_proc_remove(priv); + clean_up_m_devs(priv); + bt_free_adapter(priv); + bt_priv_put(priv); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return None + **/ +static void +sd_request_fw_callback(const struct firmware *firmware, void *context) +{ + ENTER(); + sd_request_fw_dpc(firmware, context); + LEAVE(); + return; +} + +/** + * @brief This function downloads firmware image to the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sd_download_firmware_w_helper(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + int err; + char *cur_fw_name = NULL; + + ENTER(); + + cur_fw_name = fw_name; + if (fw_name == NULL) { + if (!bt_fw_serial || priv->fw_reload || bt_fw_reload) + cur_fw_name = DEFAULT_BT_FW_NAME; + else + cur_fw_name = DEFAULT_FW_NAME; + } + + PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + GFP_KERNEL, priv, + sd_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#endif +#endif + if (ret < 0) + PRINTM(FATAL, + "BT: request_firmware_nowait() failed, error code = %#x\n", + ret); + } else { + err = request_firmware(&priv->firmware, cur_fw_name, + priv->hotplug_device); + if (err < 0) { + PRINTM(FATAL, + "BT: request_firmware() failed, error code = %#x\n", + err); + ret = BT_STATUS_FAILURE; + } else + ret = sd_request_fw_dpc(priv->firmware, priv); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function reads data from the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_card_to_host(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 buf_len = 0; + int buf_block_len; + int blksz; + struct sk_buff *skb = NULL; + u32 type; + u8 *payload = NULL; + struct hci_dev *hdev = NULL; + struct sdio_mmc_card *card = priv->bt_dev.card; + int i = 0; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type == BLUEZ_SPEC) + hdev = (struct hci_dev *)priv->bt_dev.m_dev[BT_SEQ].dev_pointer; + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + do { + /* Read the length of data to be transferred */ + ret = sd_read_rx_len(priv, &buf_len); + if (ret < 0) { + i++; + PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i); + if (i >= MAX_CMD52_RETRY) { + ret = BT_STATUS_FAILURE; + goto exit; + } + udelay(20); + } + } + while (ret == BT_STATUS_FAILURE); + + /* Allocate buffer */ + blksz = SD_BLOCK_SIZE; + buf_block_len = (buf_len + blksz - 1) / blksz; + if (buf_len <= BT_HEADER_LEN || + (buf_block_len * blksz) > ALLOC_BUF_SIZE) { + PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n", + buf_len); + ret = BT_STATUS_FAILURE; + goto exit; + } + skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + goto exit; + } + if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) { + skb_put(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + skb_pull(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + } + + payload = skb->data; + i = 0; + do { + ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: card_to_host, read iomem (%d) failed: %d\n", + i, ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) { + kfree_skb(skb); + skb = NULL; + goto exit; + } + } + } while (ret == BT_STATUS_FAILURE); + /* This is SDIO specific header length: byte[2][1][0], * type: byte[3] + (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ + buf_len = payload[0]; + buf_len |= (u16) payload[1] << 8; + type = payload[3]; + PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", hdev->name, + buf_len, type); + if (buf_len > buf_block_len * blksz) { + PRINTM(ERROR, + "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n", + buf_len, buf_block_len * blksz); + ret = BT_STATUS_FAILURE; + kfree_skb(skb); + skb = NULL; + goto exit; + } + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len); + switch (type) { + case HCI_ACLDATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (hdev) { + skb->dev = (void *)hdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + hdev->stat.byte_rx += buf_len; + } + break; + case HCI_SCODATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (hdev) { + skb->dev = (void *)hdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + hdev->stat.byte_rx += buf_len; + } + break; + case HCI_EVENT_PKT: + /** add EVT Demux */ + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb)) + break; + switch (skb->data[0]) { + case 0x0E: + /** cmd complete */ + if (hdev) { + skb->dev = (void *)hdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + hdev->stat.byte_rx += buf_len; + } + break; + case 0x0F: + /** cmd status */ + /** BT cmd status */ + if (hdev) { + skb->dev = (void *)hdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + hdev->stat.byte_rx += buf_len; + } + break; + case 0xFF: + /** Vendor specific pkt */ + /** BT EVT */ + if (hdev) { + skb->dev = (void *)hdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + hdev->stat.byte_rx += buf_len; + } + break; + default: + /** BT EVT */ + if (hdev) { + skb->dev = (void *)hdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + hdev->stat.byte_rx += buf_len; + } + break; + } + break; + case MRVL_VENDOR_PKT: + /* Just think here need to back compatible FM */ + bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (hdev) { + if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) { + skb->dev = (void *)hdev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0) + hci_recv_frame(skb); +#else + hci_recv_frame(hdev, skb); +#endif + hdev->stat.byte_rx += buf_len; + } + } + + break; + default: + /* Driver specified event and command resp should be handle + here */ + PRINTM(INFO, "BT: Unknown PKT type:%d\n", type); + kfree_skb(skb); + skb = NULL; + break; + } +exit: + if (ret) { + if (hdev) + hdev->stat.err_rx++; + PRINTM(ERROR, "error when recv pkt!\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function removes the card + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_remove_card(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + card = sdio_get_drvdata(func); + if (card) { + if (!unregister && card->priv) { + PRINTM(INFO, "BT: card removed from sd slot\n"); + ((bt_private *)(card->priv))->adapter-> + SurpriseRemoved = TRUE; + } + bt_remove_card(card->priv); + kfree(card); + } + } + + LEAVE(); +} + +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_interrupt(struct sdio_func *func) +{ + bt_private *priv; + struct m_dev *m_dev = NULL; + struct sdio_mmc_card *card; + int ret = BT_STATUS_SUCCESS; + u8 ireg = 0; + u8 host_intstatus_reg = HOST_INTSTATUS_REG; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->priv) { + PRINTM(INFO, + "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", + __func__, func, card); + LEAVE(); + return; + } + priv = card->priv; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0, SD_BLOCK_SIZE); + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n", + ret); + goto done; + } + ireg = priv->adapter->hw_regs[host_intstatus_reg]; + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n", + ret); + goto done; + } + if (ireg != 0) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * Clear the interrupt status register and re-enable + * the interrupt + */ + PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name, + ireg); + priv->adapter->irq_recv = ireg; + } else { + PRINTM(ERROR, "BT: ERR: ireg=0\n"); + } + OS_INT_DISABLE; + priv->adapter->sd_ireg |= ireg; + OS_INT_RESTORE; + bt_interrupt(m_dev); +done: + LEAVE(); +} + +/** + * @brief This function checks if the interface is ready to download + * or not while other download interfaces are present + * + * @param priv A pointer to bt_private structure + * @param val Winner status (0: winner) + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_check_winner_status(bt_private *priv, u8 *val) +{ + + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = CARD_FW_STATUS0_REG; + + ENTER(); + winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret); + if (ret != BT_STATUS_SUCCESS) { + LEAVE(); + return BT_STATUS_FAILURE; + } + *val = winner; + + LEAVE(); + return ret; +} + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** @brief This function tells lower driver that BT is suspended + * + * @param priv A pointer to bt_private structure + * @return None + */ +void +bt_is_suspended(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + priv->adapter->is_suspended = TRUE; + sdio_func_suspended(card->func); +} +#endif + +/** @brief This function handles client driver suspend + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +bt_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + struct hci_dev *hcidev; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name); + hcidev = (struct hci_dev *)m_dev->dev_pointer; + hci_suspend_dev(hcidev); + skb_queue_purge(&priv->adapter->tx_queue); + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, "BT: HS not actived, suspend fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to suspend!\n"); + } + } + } + + priv->adapter->is_suspended = TRUE; + + LEAVE(); + /* We will keep the power when hs enabled successfully */ + if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) { +#ifdef MMC_PM_SKIP_RESUME_PROBE + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and " + "MMC_PM_SKIP_RESUME_PROBE\n"); + return sdio_set_host_pm_flags(func, + MMC_PM_KEEP_POWER | + MMC_PM_SKIP_RESUME_PROBE); +#else + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n"); + return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +#endif + } else { + PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n"); + return BT_STATUS_SUCCESS; + } +} + +void +bt_sdio_shutdown(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is shutdown\n", + sdio_func_id(func)); + return; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return; + } + + priv = cardp->priv; + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, "BT: HS not actived, shutdown fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to shutdown!\n"); + } + } + } + LEAVE(); +} + +/** @brief This function handles client driver resume + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS + */ +int +bt_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + struct hci_dev *hcidev; + + ENTER(); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + priv->adapter->is_suspended = FALSE; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name); + hcidev = (struct hci_dev *)m_dev->dev_pointer; + hci_resume_dev(hcidev); + sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name); + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif +#endif + +/******************************************************** + Global Functions +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +static const struct dev_pm_ops bt_sdio_pm_ops = { + .suspend = bt_sdio_suspend, + .resume = bt_sdio_resume, +}; +#endif +#endif +static struct sdio_driver sdio_bt = { + .name = "sdio_bt", + .id_table = bt_ids, + .probe = sd_probe_card, + .remove = sd_remove_card, +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &bt_sdio_pm_ops, + .shutdown = bt_sdio_shutdown, + } +#endif +#endif +}; + +/** + * @brief This function registers the bt module in bus driver. + * + * @return An int pointer that keeps returned value + */ +int * +sbi_register(void) +{ + int *ret; + + ENTER(); + + if (sdio_register_driver(&sdio_bt) != 0) { + PRINTM(FATAL, "BT: SD Driver Registration Failed\n"); + LEAVE(); + return NULL; + } else + ret = (int *)1; + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the bt module in bus driver. + * + * @return N/A + */ +void +sbi_unregister(void) +{ + ENTER(); + unregister = TRUE; + sdio_unregister_driver(&sdio_bt); + LEAVE(); +} + +/** + * @brief This function registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_dev(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + u8 chiprev; + struct sdio_mmc_card *card = priv->bt_dev.card; + struct sdio_func *func; + u8 host_intstatus_reg = HOST_INTSTATUS_REG; + u8 host_int_rsr_reg = HOST_INT_RSR_REG; + u8 card_misc_cfg_reg = CARD_MISC_CFG_REG; + u8 card_revision_reg = CARD_REVISION_REG; + u8 io_port_0_reg = IO_PORT_0_REG; + u8 io_port_1_reg = IO_PORT_1_REG; + u8 io_port_2_reg = IO_PORT_2_REG; + u8 card_magic_reg = CARD_MAGIC_REG; + u8 magic_val = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: Error: card or function is NULL!\n"); + goto failed; + } + func = card->func; + priv->hotplug_device = &func->dev; + + /* Initialize the private structure */ + strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name)); + priv->bt_dev.ioport = 0; + priv->bt_dev.fn = func->num; + + sdio_claim_host(func); + ret = sdio_claim_irq(func, sd_interrupt); + if (ret) { + PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret); + goto release_host; + } + ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); + if (ret) { + PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__); + goto release_irq; + } + + /* read Revision Register to get the chip revision number */ + chiprev = sdio_readb(func, card_revision_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n"); + goto release_irq; + } + priv->adapter->chip_rev = chiprev; + PRINTM(INFO, "revision=%#x\n", chiprev); + + magic_val = sdio_readb(func, card_magic_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n"); + goto release_irq; + } + priv->adapter->magic_val = magic_val; + PRINTM(INFO, "magic_val=%#x\n", magic_val); + + /* + * Read the HOST_INTSTATUS_REG for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + reg = sdio_readb(func, host_intstatus_reg, &ret); + if (ret < 0) + goto release_irq; + + /* Read the IO port */ + reg = sdio_readb(func, io_port_0_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= reg; + + reg = sdio_readb(func, io_port_1_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 8); + + reg = sdio_readb(func, io_port_2_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 16); + + PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn, + priv->bt_dev.ioport); + +#define SDIO_INT_MASK 0x3F + /* Set Host interrupt reset to read to clear */ + reg = sdio_readb(func, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + /* Set auto re-enable */ + reg = sdio_readb(func, card_misc_cfg_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg, &ret); + if (ret < 0) + goto release_irq; + + sdio_set_drvdata(func, card); + sdio_release_host(func); + + LEAVE(); + return BT_STATUS_SUCCESS; +release_irq: + sdio_release_irq(func); +release_host: + sdio_release_host(func); +failed: + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function de-registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_unregister_dev(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + if (card && card->func) { + sdio_claim_host(card->func); + sdio_release_irq(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + sdio_set_drvdata(card->func, NULL); + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_enable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sd_enable_host_int_mask(priv, HIM_ENABLE); + sd_get_rx_unit(priv); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sd_disable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sd_disable_host_int_mask(priv, HIM_DISABLE); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param priv A pointer to bt_private structure + * @param payload A pointer to the data/cmd buffer + * @param nb Length of data/cmd + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + int ret = BT_STATUS_SUCCESS; + int buf_block_len; + int blksz; + int i = 0; + u8 *buf = NULL; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + buf = payload; + + blksz = SD_BLOCK_SIZE; + buf_block_len = (nb + blksz - 1) / blksz; + /* Allocate buffer and copy payload */ + if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) { + if (nb > MAX_TX_BUF_SIZE) { + PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb); + LEAVE(); + return BT_STATUS_FAILURE; + } + /* Ensure 8-byte aligned CMD buffer */ + buf = priv->adapter->tx_buf; + memcpy(buf, payload, nb); + } + sdio_claim_host(card->func); + do { + /* Transfer data to card */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: host_to_card, write iomem (%d) failed: %d\n", + i, ret); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) + goto exit; + } else { + PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n", + m_dev->name, nb); + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb); + } + } while (ret == BT_STATUS_FAILURE); + priv->bt_dev.tx_dnld_rdy = FALSE; +exit: + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function downloads firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_download_fw(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + sdio_claim_host(card->func); + if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) { + PRINTM(MSG, "BT: FW already downloaded!\n"); + sdio_release_host(card->func); + sd_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + /* Check if other interface is downloading */ + ret = sd_check_winner_status(priv, &winner); + if (ret == BT_STATUS_FAILURE) { + PRINTM(FATAL, "BT read winner status failed!\n"); + goto done; + } + if (winner) { + PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n", + winner); + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) { + PRINTM(FATAL, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + sdio_release_host(card->func); + sd_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + + do_gettimeofday(&priv->req_fw_time); + /* Download the main firmware via the helper firmware */ + if (sd_download_firmware_w_helper(priv)) { + PRINTM(INFO, "BT: FW download failed!\n"); + ret = BT_STATUS_FAILURE; + } + goto exit; +done: + sdio_release_host(card->func); +exit: + LEAVE(); + return ret; +err_register: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the interrupt status and handle it accordingly. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_get_int_status(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 sdio_ireg = 0; + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + OS_INT_DISABLE; + sdio_ireg = priv->adapter->sd_ireg; + priv->adapter->sd_ireg = 0; + OS_INT_RESTORE; + sdio_claim_host(card->func); + priv->adapter->irq_done = sdio_ireg; + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ + if (priv->bt_dev.tx_dnld_rdy) { /* tx_done already received */ + PRINTM(INFO, + "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", + priv->bt_dev.tx_dnld_rdy, sdio_ireg); + } else { + priv->bt_dev.tx_dnld_rdy = TRUE; + } + } + if (sdio_ireg & UP_LD_HOST_INT_STATUS) + sd_card_to_host(priv); + + ret = BT_STATUS_SUCCESS; + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function wakeup firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_wakeup_firmware(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + sdio_release_host(card->func); + PRINTM(CMD, "BT wake up firmware\n"); + + LEAVE(); + return ret; +} + +#define INIT_START_REG 0xF1 +#define INIT_END_REG 0xF6 + +/** @brief This function dump the SDIO register + * + * @param priv A Pointer to the bt_private structure + * + * @return N/A + */ +void +bt_dump_sdio_regs(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + char buf[256], *ptr; + u8 loop, func, data; + unsigned int reg, reg_start, reg_end; + u8 loop_num = 2; + unsigned int init_reg_start = 0; + unsigned int init_reg_end = 0; + init_reg_start = INIT_START_REG; + init_reg_end = INIT_END_REG; + + if (priv->adapter->ps_state) + sbi_wakeup_firmware(priv); + + sdio_claim_host(card->func); + for (loop = 0; loop < loop_num; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else { + func = 2; + reg_start = 0; + reg_end = 0x09; + } + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, reg_start, + reg_end); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(card->func, reg, &ret); + else + data = sdio_readb(card->func, reg, &ret); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + + if (init_reg_start) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ", + init_reg_start, init_reg_end); + for (reg = init_reg_start; reg <= init_reg_end;) { + data = sdio_readb(card->func, reg, &ret); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + sdio_release_host(card->func); +} + +module_param(fw_name, charp, 0); +MODULE_PARM_DESC(fw_name, "Firmware name"); +module_param(bt_req_fw_nowait, int, 0); +MODULE_PARM_DESC(bt_req_fw_nowait, + "0: Use request_firmware API; 1: Use request_firmware_nowait API"); +module_param(multi_fn, int, 0); +MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
diff --git a/bt_sd8997/bt/hci_wrapper.h b/bt_sd8997/bt/hci_wrapper.h new file mode 100644 index 0000000..e2942de --- /dev/null +++ b/bt_sd8997/bt/hci_wrapper.h
@@ -0,0 +1,162 @@ +/** @file hci_wrapper.h + * @brief This file contains HCI related definitions + * + * Copyright (C) 2011-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _HCI_WRAPPER_H_ +#define _HCI_WRAPPER_H_ + +#include <linux/module.h> +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +/** Define Seq num */ +#define BT_SEQ 0 + +/** Define dev type */ +#define BT_TYPE 1 +#define BT_AMP_TYPE 2 + +/** Define spec type */ +#define BLUEZ_SPEC 1 +#define IANYWHERE_SPEC 2 +#define GENERIC_SPEC 3 + +/** Define lock/unlock wrapper */ +#define mdev_req_lock(d) down(&d->req_lock) +#define mdev_req_unlock(d) up(&d->req_lock) + +/** Length of device name */ +#define DEV_NAME_LEN 32 + +/** Define struct m_dev */ +struct m_dev { + char name[DEV_NAME_LEN]; + int index; + unsigned long flags; + spinlock_t lock; + struct semaphore req_lock; + struct sk_buff_head rx_q; + wait_queue_head_t req_wait_q; + struct hci_dev_stats stat; + struct module *owner; + void *dev_pointer; + int dev_type; + int spec_type; + void *driver_data; + int read_continue_flag; + int wait_rx_complete; + int rx_complete_flag; + wait_queue_head_t rx_wait_q; + spinlock_t rxlock; + + struct sk_buff *evt_skb; + struct sk_buff *acl_skb; + struct sk_buff *sco_skb; + + int (*open) (struct m_dev * m_dev); + int (*close) (struct m_dev * m_dev); + int (*flush) (struct m_dev * m_dev); + int (*send) (struct m_dev * m_dev, struct sk_buff * skb); + void (*destruct) (struct m_dev * m_dev); + void (*notify) (struct m_dev * m_dev, unsigned int evt); + int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg); + void (*query) (struct m_dev * m_dev, void *arg); + +}; + +/** Define struct mbt_dev */ +struct mbt_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; + __u8 type; + + __u16 pkt_type; + __u16 esco_type; + __u16 link_policy; + __u16 link_mode; + + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + + struct sk_buff *reassembly[3]; + + atomic_t promisc; +}; + +/** This function frees m_dev allocation */ +void free_m_dev(struct m_dev *m_dev); + +/** + * @brief This function receives frames + * + * @param skb A pointer to struct sk_buff + * @return 0--success otherwise error code + */ +static inline int +mdev_recv_frame(struct sk_buff *skb) +{ + struct m_dev *m_dev = (struct m_dev *)skb->dev; + if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags) + && !test_bit(HCI_INIT, &m_dev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Incomming skb */ + bt_cb(skb)->incoming = 1; + + /* Time stamp */ + __net_timestamp(skb); + + /* Queue frame for rx task */ + skb_queue_tail(&m_dev->rx_q, skb); + + /* Wakeup rx thread */ + wake_up_interruptible(&m_dev->req_wait_q); + + return 0; +} + +/** + * @brief mbt dev suspend handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_suspend_dev(struct m_dev *m_dev) +{ + return 0; +} + +/** + * @brief mbt dev resume handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_resume_dev(struct m_dev *m_dev) +{ + return 0; +} + +#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8997/bt/mbt_char.c b/bt_sd8997/bt/mbt_char.c new file mode 100644 index 0000000..c0c025f --- /dev/null +++ b/bt_sd8997/bt/mbt_char.c
@@ -0,0 +1,774 @@ +/** @file mbt_char.c + * + * @brief This file contains the char device function calls + * + * Copyright (C) 2010-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/path.h> +#include <linux/namei.h> +#include <linux/mount.h> + +#include "bt_drv.h" +#include "mbt_char.h" + +static LIST_HEAD(char_dev_list); + +static DEFINE_SPINLOCK(char_dev_list_lock); + +static int mbtchar_major = MBTCHAR_MAJOR_NUM; + +struct kobject * +chardev_get(struct char_dev *dev) +{ + struct kobject *kobj; + + kobj = bt_priv_get(dev->m_dev->driver_data); + if (!kobj) + return NULL; + PRINTM(INFO, "dev get kobj\n"); + kobj = kobject_get(&dev->kobj); + if (!kobj) + bt_priv_put(dev->m_dev->driver_data); + return kobj; +} + +void +chardev_put(struct char_dev *dev) +{ + if (dev) { + struct m_dev *m_dev = dev->m_dev; + PRINTM(INFO, "dev put kobj\n"); + kobject_put(&dev->kobj); + if (m_dev) + bt_priv_put(m_dev->driver_data); + } +} + +/** + * @brief Changes permissions of the dev + * + * @param name pointer to character + * @param mode mode_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chmod(char *name, mode_t mode) +{ + struct path path; + struct inode *inode; + struct iattr newattrs; + int ret; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chmod(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief Changes ownership of the dev + * + * @param name pointer to character + * @param user uid_t type data + * @param group gid_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chown(char *name, uid_t user, gid_t group) +{ + struct path path; + struct inode *inode = NULL; + struct iattr newattrs; + int ret = 0; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chown(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_valid = ATTR_CTIME; + if (user != (uid_t) (-1)) { + newattrs.ia_valid |= ATTR_UID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_uid = user; +#else + newattrs.ia_uid = KUIDT_INIT(user); +#endif + } + if (group != (gid_t) (-1)) { + newattrs.ia_valid |= ATTR_GID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_gid = group; +#else + newattrs.ia_gid = KGIDT_INIT(group); +#endif + } + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief write handler for char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes written + */ +ssize_t +chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos) +{ + int nwrite = 0; + struct sk_buff *skb; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + if (!test_bit(HCI_UP, &m_dev->flags)) { + LEAVE(); + return -EBUSY; + } + nwrite = count; + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n"); + LEAVE(); + return -ENOMEM; + } + + if (copy_from_user((void *)skb_put(skb, count), buf, count)) { + PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n"); + kfree_skb(skb); + nwrite = -EFAULT; + goto exit; + } + + skb->dev = (void *)m_dev; + bt_cb(skb)->pkt_type = *((unsigned char *)skb->data); + skb_pull(skb, 1); + + PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len); + + /* Send skb to the hci wrapper layer */ + if (m_dev->send(m_dev, skb)) { + PRINTM(ERROR, "Write: Fail\n"); + nwrite = 0; + /* Send failed */ + kfree_skb(skb); + } +exit: + LEAVE(); + return nwrite; +} + +/** + * @brief read handler for BT char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes read + */ +ssize_t +chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos) +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + /* Wait for rx data */ + add_wait_queue(&m_dev->req_wait_q, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + skb = skb_dequeue(&m_dev->rx_q); + if (skb) + break; + if (!test_bit(HCI_UP, &m_dev->flags)) { + ret = -EBUSY; + break; + } + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&m_dev->req_wait_q, &wait); + + if (!skb) + goto out; + + if (m_dev->read_continue_flag == 0) { + /* Put type byte before the data */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + } + DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len); + if (skb->len > count) { + /* user data length is smaller than the skb length */ + if (copy_to_user(buf, skb->data, count)) { + ret = -EFAULT; + goto outf; + } + skb_pull(skb, count); + skb_queue_head(&m_dev->rx_q, skb); + m_dev->read_continue_flag = 1; + wake_up_interruptible(&m_dev->req_wait_q); + ret = count; + goto out; + } else { + if (copy_to_user(buf, skb->data, skb->len)) { + ret = -EFAULT; + goto outf; + } + m_dev->read_continue_flag = 0; + ret = skb->len; + } +outf: + kfree_skb(skb); +out: + if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) { + m_dev->rx_complete_flag = TRUE; + wake_up_interruptible(&m_dev->rx_wait_q); + } + LEAVE(); + return ret; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl common handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg) +#else +/** + * @brief ioctl common handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +char_ioctl(struct file *filp, unsigned int cmd, void *arg) +#endif +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { + case MBTCHAR_IOCTL_RELEASE: + m_dev->close(m_dev); + break; + case MBTCHAR_IOCTL_QUERY_TYPE: + m_dev->query(m_dev, arg); + break; + default: + m_dev->ioctl(m_dev, cmd, arg); + break; + } + LEAVE(); + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, (void *)arg); +#else + return char_ioctl(filp, cmd, (void *)arg); +#endif +} + +#ifdef CONFIG_COMPAT +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief compat ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl_compat(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief compat ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, compat_ptr(arg)); +#else + return char_ioctl(filp, cmd, compat_ptr(arg)); +#endif +} +#endif /* CONFIG_COMPAT */ + +/** + * @brief open handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = NULL; + struct m_dev *m_dev = NULL; + struct char_dev *cdev = NULL; + struct list_head *p = NULL; + ENTER(); + + list_for_each(p, &char_dev_list) { + cdev = list_entry(p, struct char_dev, list); + if (mbtchar_major == MAJOR(inode->i_cdev->dev) && + cdev->minor == MINOR(inode->i_cdev->dev)) { + dev = cdev; + break; + } + } + if (!dev) { + PRINTM(ERROR, "cannot find dev from inode\n"); + LEAVE(); + return -ENXIO; + } + if (!chardev_get(dev)) { + LEAVE(); + return -ENXIO; + } + filp->private_data = dev; /* for other methods */ + m_dev = dev->m_dev; + mdev_req_lock(m_dev); + if (test_bit(HCI_UP, &m_dev->flags)) { + ret = -EALREADY; + goto done; + } + if (m_dev->open(m_dev)) { + ret = -EIO; + goto done; + } + set_bit(HCI_UP, &m_dev->flags); + +done: + mdev_req_unlock(m_dev); + if (ret) + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief release handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + if (m_dev) + ret = dev->m_dev->close(dev->m_dev); + filp->private_data = NULL; + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief poll handler for char dev + * + * @param filp pointer to structure file + * @param wait pointer to poll_table structure + * @return mask + */ +static unsigned int +chardev_poll(struct file *filp, poll_table * wait) +{ + unsigned int mask; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + + m_dev = dev->m_dev; + poll_wait(filp, &m_dev->req_wait_q, wait); + mask = POLLOUT | POLLWRNORM; + if (skb_peek(&m_dev->rx_q)) + mask |= POLLIN | POLLRDNORM; + if (!test_bit(HCI_UP, &(m_dev->flags))) + mask |= POLLHUP; + PRINTM(INFO, "poll mask=0x%x\n", mask); + LEAVE(); + return mask; +} + +/* File ops for the Char driver */ +const struct file_operations chardev_fops = { + .owner = THIS_MODULE, + .read = chardev_read, + .write = chardev_write, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + .ioctl = chardev_ioctl, +#else + .unlocked_ioctl = chardev_ioctl, +#endif +#ifdef CONFIG_COMPAT + .compat_ioctl = chardev_ioctl_compat, +#endif + .open = chardev_open, + .release = chardev_release, + .poll = chardev_poll, +}; + +/** + * @brief This function creates the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param mod_name A pointer to char + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name) +{ + int ret = 0, dev_num; + unsigned long flags; + ENTER(); + /* create the chrdev region */ + if (mbtchar_major) { + dev_num = MKDEV(mbtchar_major, dev->minor); + ret = register_chrdev_region(dev_num, 1, mod_name); + } else { + PRINTM(INFO, "chardev: no major # yet\n"); + ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1, + mod_name); + } + + if (ret) { + PRINTM(ERROR, "chardev: create chrdev_region failed\n"); + LEAVE(); + return ret; + } + if (!mbtchar_major) { + /* Store the allocated dev major # */ + mbtchar_major = MAJOR(dev_num); + } + dev->cdev = cdev_alloc(); + dev->cdev->ops = &chardev_fops; + dev->cdev->owner = chardev_fops.owner; + dev_num = MKDEV(mbtchar_major, dev->minor); + + if (cdev_add(dev->cdev, dev_num, 1)) { + PRINTM(ERROR, "chardev: cdev_add failed\n"); + ret = -EFAULT; + goto free_cdev_region; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } +#else + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } +#endif + PRINTM(INFO, "register char dev=%s\n", dev_name); + + /** modify later */ + + spin_lock_irqsave(&char_dev_list_lock, flags); + list_add_tail(&dev->list, &char_dev_list); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + + LEAVE(); + return ret; +free_cdev_region: + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + LEAVE(); + return ret; +} + +/** + * @brief This function deletes the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name) +{ + ENTER(); + device_destroy(char_class, MKDEV(mbtchar_major, dev->minor)); + cdev_del(dev->cdev); + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + PRINTM(INFO, "unregister char dev=%s\n", dev_name); + + LEAVE(); + return 0; +} + +/** + * @brief This function cleans module + * + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup(struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + do { + dev = NULL; + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + unregister_char_dev(dev, char_class, dev->m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } while (dev); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + class_destroy(char_class); + LEAVE(); +} + +/** + * @brief This function cleans module + * + * @param m_dev A pointer to m_dev struct + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + if (dev->minor == m_dev->index) { + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + dev->m_dev = NULL; + unregister_char_dev(dev, char_class, m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } + spin_unlock_irqrestore(&char_dev_list_lock, flags); + LEAVE(); +}
diff --git a/bt_sd8997/bt/mbt_char.h b/bt_sd8997/bt/mbt_char.h new file mode 100644 index 0000000..884d259 --- /dev/null +++ b/bt_sd8997/bt/mbt_char.h
@@ -0,0 +1,70 @@ +/** @file mbt_char.h + * + * @brief This file contains mbtchar driver specific defines etc + * + * Copyright (C) 2010-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifndef __MBT_CHAR_H__ +#define __MBT_CHAR_H__ + +#include <linux/cdev.h> +#include <linux/device.h> + +/** Define ioctl */ +#define MBTCHAR_IOCTL_RELEASE _IO('M', 1) +#define MBTCHAR_IOCTL_QUERY_TYPE _IO('M', 2) +#ifdef BLE_WAKEUP +#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4) +#endif + +#define MBTCHAR_MAJOR_NUM (0) + +/** Interface specific macros */ +#define FMCHAR_MINOR_BASE (10) +#define NFCCHAR_MINOR_BASE (20) + +/** Declaration of char_dev struct */ +struct char_dev { + struct list_head list; + int minor; + int dev_type; + struct cdev *cdev; + struct m_dev *m_dev; + struct kobject kobj; +}; + +/** Changes permissions of the dev */ +int mbtchar_chmod(char *name, mode_t mode); + +/** Changes ownership of the dev */ +int mbtchar_chown(char *name, uid_t user, gid_t group); + +/** This function creates the char dev */ +int register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name); + +/** This function deletes the char dev */ +int unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name); + +/** This function cleans module */ +void chardev_cleanup(struct class *char_class); + +/** This function cleans module */ +void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class); + +#endif /*__MBT_CHAR_H__*/
diff --git a/bt_sd8997/bt_char/bt_drv.h b/bt_sd8997/bt_char/bt_drv.h new file mode 100644 index 0000000..8d8b659 --- /dev/null +++ b/bt_sd8997/bt_char/bt_drv.h
@@ -0,0 +1,936 @@ +/** @file bt_drv.h + * @brief This header file contains global constant/enum definitions, + * global variable declaration. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_DRV_H_ +#define _BT_DRV_H_ + +#include <linux/version.h> +#include <linux/kthread.h> +#include <linux/skbuff.h> +#include <linux/vmalloc.h> + +#include "hci_wrapper.h" + +/** MAX adapter BT driver supported */ +#define MAX_BT_ADAPTER 3 + +#ifndef BIT +/** BIT definition */ +#define BIT(x) (1UL << (x)) +#endif + +#ifdef MBT_64BIT +typedef u64 t_ptr; +#else +typedef u32 t_ptr; +#endif + +/** max number of adapter supported */ +#define MAX_BT_ADAPTER 3 +/** Define drv_mode bit */ +#define DRV_MODE_BT BIT(0) + +/** Define devFeature bit */ +#define DEV_FEATURE_BT BIT(0) +#define DEV_FEATURE_BTAMP BIT(1) +#define DEV_FEATURE_BLE BIT(2) + +/** Define maximum number of radio func supported */ +#define MAX_RADIO_FUNC 4 + +/** MAC address print format */ +#ifndef MACSTR +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif + +/** MAC address print arguments */ +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Debug level : Message */ +#define DBG_MSG BIT(0) +/** Debug level : Fatal */ +#define DBG_FATAL BIT(1) +/** Debug level : Error */ +#define DBG_ERROR BIT(2) +/** Debug level : Data */ +#define DBG_DATA BIT(3) +/** Debug level : Command */ +#define DBG_CMD BIT(4) +/** Debug level : Event */ +#define DBG_EVENT BIT(5) +/** Debug level : Interrupt */ +#define DBG_INTR BIT(6) + +/** Debug entry : Data dump */ +#define DBG_DAT_D BIT(16) +/** Debug entry : Data dump */ +#define DBG_CMD_D BIT(17) + +/** Debug level : Entry */ +#define DBG_ENTRY BIT(28) +/** Debug level : Warning */ +#define DBG_WARN BIT(29) +/** Debug level : Informative */ +#define DBG_INFO BIT(30) + +#ifdef DEBUG_LEVEL1 +extern u32 mbt_drvdbg; + +#ifdef DEBUG_LEVEL2 +/** Print informative message */ +#define PRINTM_INFO(msg...) \ + do {if (mbt_drvdbg & DBG_INFO) \ + printk(KERN_DEBUG msg); } while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) \ + do {if (mbt_drvdbg & DBG_WARN) \ + printk(KERN_DEBUG msg); } while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) \ + do {if (mbt_drvdbg & DBG_ENTRY) \ + printk(KERN_DEBUG msg); } while (0) +#else +/** Print informative message */ +#define PRINTM_INFO(msg...) do {} while (0) +/** Print warning message */ +#define PRINTM_WARN(msg...) do {} while (0) +/** Print entry message */ +#define PRINTM_ENTRY(msg...) do {} while (0) +#endif /* DEBUG_LEVEL2 */ + +/** Print interrupt message */ +#define PRINTM_INTR(msg...) \ + do {if (mbt_drvdbg & DBG_INTR) \ + printk(KERN_DEBUG msg); } while (0) +/** Print event message */ +#define PRINTM_EVENT(msg...) \ + do {if (mbt_drvdbg & DBG_EVENT) \ + printk(KERN_DEBUG msg); } while (0) +/** Print command message */ +#define PRINTM_CMD(msg...) \ + do {if (mbt_drvdbg & DBG_CMD) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data message */ +#define PRINTM_DATA(msg...) \ + do {if (mbt_drvdbg & DBG_DATA) \ + printk(KERN_DEBUG msg); } while (0) +/** Print error message */ +#define PRINTM_ERROR(msg...) \ + do {if (mbt_drvdbg & DBG_ERROR) \ + printk(KERN_ERR msg); } while (0) +/** Print fatal message */ +#define PRINTM_FATAL(msg...) \ + do {if (mbt_drvdbg & DBG_FATAL) \ + printk(KERN_ERR msg); } while (0) +/** Print message */ +#define PRINTM_MSG(msg...) \ + do {if (mbt_drvdbg & DBG_MSG) \ + printk(KERN_ALERT msg); } while (0) + +/** Print data dump message */ +#define PRINTM_DAT_D(msg...) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + printk(KERN_DEBUG msg); } while (0) +/** Print data dump message */ +#define PRINTM_CMD_D(msg...) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + printk(KERN_DEBUG msg); } while (0) + +/** Print message with required level */ +#define PRINTM(level, msg...) PRINTM_##level(msg) + +/** Debug dump buffer length */ +#define DBG_DUMP_BUF_LEN 64 +/** Maximum number of dump per line */ +#define MAX_DUMP_PER_LINE 16 +/** Maximum data dump length */ +#define MAX_DATA_DUMP_LEN 48 + +/** + * @brief Prints buffer data upto provided length + * + * @param prompt Char pointer + * @param buf Buffer + * @param len Length + * + * @return N/A + */ +static inline void +hexdump(char *prompt, u8 *buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + printk(KERN_DEBUG "%s: len=%d\n", prompt, len); + for (i = 1; i <= len; i++) { + ptr += snprintf(ptr, 4, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +/** Debug hexdump of debug data */ +#define DBG_HEXDUMP_DAT_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_DAT_D) \ + hexdump(x, y, z); } while (0) +/** Debug hexdump of debug command */ +#define DBG_HEXDUMP_CMD_D(x, y, z) \ + do {if (mbt_drvdbg & DBG_CMD_D) \ + hexdump(x, y, z); } while (0) + +/** Debug hexdump */ +#define DBG_HEXDUMP(level, x, y, z) DBG_HEXDUMP_##level(x, y, z) + +/** Mark entry point */ +#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +/** Mark exit point */ +#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __func__, \ + __FILE__, __LINE__) +#else +/** Do nothing */ +#define PRINTM(level, msg...) do {} while (0) +/** Do nothing */ +#define DBG_HEXDUMP(level, x, y, z) do {} while (0) +/** Do nothing */ +#define ENTER() do {} while (0) +/** Do nothing */ +#define LEAVE() do {} while (0) +#endif /* DEBUG_LEVEL1 */ + +/** Bluetooth upload size */ +#define BT_UPLD_SIZE 2312 +/** Bluetooth status success */ +#define BT_STATUS_SUCCESS (0) +/** Bluetooth status pending */ +#define BT_STATUS_PENDING (1) +/** Bluetooth status failure */ +#define BT_STATUS_FAILURE (-1) + +#ifndef TRUE +/** True value */ +#define TRUE 1 +#endif +#ifndef FALSE +/** False value */ +#define FALSE 0 +#endif + +/** Set thread state */ +#define OS_SET_THREAD_STATE(x) set_current_state(x) +/** Time to wait until Host Sleep state change in millisecond */ +#define WAIT_UNTIL_HS_STATE_CHANGED 2000 +/** Time to wait cmd resp in millisecond */ +#define WAIT_UNTIL_CMD_RESP 5000 + +/** Sleep until a condition gets true or a timeout elapses */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + interruptible_sleep_on_timeout(&waitq, ((timeout) * HZ / 1000)) +#else +#define os_wait_interruptible_timeout(waitq, cond, timeout) \ + wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) +#endif + +/** bt thread structure */ +typedef struct { + /** Task */ + struct task_struct *task; + /** Queue */ + wait_queue_head_t waitQ; + /** PID */ + pid_t pid; + /** Private structure */ + void *priv; +} bt_thread; + +/** + * @brief Activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_activate_thread(bt_thread *thr) +{ + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitQ); + + /** Record the thread pid */ + thr->pid = current->pid; +} + +/** + * @brief De-activates bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline void +bt_deactivate_thread(bt_thread *thr) +{ + thr->pid = 0; + return; +} + +/** + * @brief Creates bt thread + * + * @param btfunc Function pointer + * @param thr A pointer to bt_thread structure + * @param name Char pointer + * + * @return N/A + */ +static inline void +bt_create_thread(int (*btfunc) (void *), bt_thread *thr, char *name) +{ + thr->task = kthread_run(btfunc, thr, "%s", name); +} + +/** + * @brief Delete bt thread + * + * @param thr A pointer to bt_thread structure + * + * @return N/A + */ +static inline int +bt_terminate_thread(bt_thread *thr) +{ + /* Check if the thread is active or not */ + if (!thr->pid) + return -1; + + kthread_stop(thr->task); + return 0; +} + +/** + * @brief Set scheduled timeout + * + * @param millisec Time unit in ms + * + * @return N/A + */ +static inline void +os_sched_timeout(u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + + schedule_timeout((millisec * HZ) / 1000); +} + +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__((packed)) +#endif + +/** BT histogram command */ +#define BT_CMD_HISTOGRAM 0xEA +/** max antenna num */ +#define MAX_ANTENNA_NUM 2 +/** BDR 1M */ +#define BDR_RATE_1M 1 +/** EDR 2/3 M */ +#define EDR_RATE_2_3M 2 +/** BLE 1M */ +#define BLE_RATE_1M 5 +/** max bt link number */ +#define MAX_BT_LINK 10 +/** max ble link number */ +#define MAX_BLE_LINK 16 + +/** BT link state structure */ +typedef struct _bt_link_stat { + /** txrx rate 1: BDR_1M, 2:EDR 2/3 M, 3:BLE 1M */ + u8 txrxrate; + /** power: -30 = N = 20 dbm*/ + s8 txpower; + /** rssi: -127 to +20 (For BT), -128 to +127 (For BLE) */ + s8 rssi; +} __ATTRIB_PACK__ bt_link_stat; + +/** BT histogram data structure */ +typedef struct _bt_histogram_data { + /** Antenna */ + u8 antenna; + /** Powerclass */ + u8 powerclass; + /** bt link state structure */ + bt_link_stat link[MAX_BT_LINK + MAX_BLE_LINK]; +} __ATTRIB_PACK__ bt_histogram_data; + +/** BT histogram proc data structure */ +typedef struct _bt_hist_proc_data { + /** antenna */ + u8 antenna; + /** Private structure */ + struct _bt_private *pbt; +} bt_hist_proc_data; + +/** Data structure for the Marvell Bluetooth device */ +typedef struct _bt_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** m_dev structure */ + struct m_dev m_dev[MAX_RADIO_FUNC]; + + /** Tx download ready flag */ + u8 tx_dnld_rdy; + /** Function */ + u8 fn; + /** Rx unit */ + u8 rx_unit; + /** Power Save mode : Timeout configuration */ + u16 idle_timeout; + /** Power Save mode */ + u8 psmode; + /** Power Save command */ + u8 pscmd; + /** Host Sleep mode */ + u8 hsmode; + /** Host Sleep command */ + u8 hscmd; + /** Low byte is gap, high byte is GPIO */ + u16 gpio_gap; + /** Host Sleep configuration command */ + u8 hscfgcmd; + /** Host Send Cmd Flag */ + u8 sendcmdflag; + /** opcode for Send Cmd */ + u16 send_cmd_opcode; + /** Device Type */ + u8 devType; + /** Device Features */ + u8 devFeature; + /** cmd52 function */ + u8 cmd52_func; + /** cmd52 register */ + u8 cmd52_reg; + /** cmd52 value */ + u8 cmd52_val; + /** SDIO pull control command */ + u8 sdio_pull_ctrl; + /** Low 2 bytes is pullUp, high 2 bytes for pull-down */ + u32 sdio_pull_cfg; + /** Test mode command */ + u8 test_mode; +} bt_dev_t, *pbt_dev_t; + +/** Marvell bt adapter structure */ +typedef struct _bt_adapter { + /** Chip revision ID */ + u8 chip_rev; + /** magic val */ + u8 magic_val; + /** Surprise removed flag */ + u8 SurpriseRemoved; + /** IRQ number */ + int irq; + /** Interrupt counter */ + u32 IntCounter; + /** Tx packet queue */ + struct sk_buff_head tx_queue; + + /** Pointer of fw dump file name */ + char *fwdump_fname; + /** Fw dump queue */ + struct sk_buff_head fwdump_queue; + /** Pending Tx packet queue */ + struct sk_buff_head pending_queue; + /** tx lock flag */ + u8 tx_lock; + /** Power Save mode */ + u8 psmode; + /** Power Save state */ + u8 ps_state; + /** Host Sleep state */ + u8 hs_state; + /** hs skip count */ + u32 hs_skip; + /** suspend_fail flag */ + u8 suspend_fail; + /** suspended flag */ + u8 is_suspended; + /** Number of wakeup tries */ + u8 WakeupTries; + /** Host Sleep wait queue */ + wait_queue_head_t cmd_wait_q __ATTRIB_ALIGN__; + /** Host Cmd complet state */ + u8 cmd_complete; + /** last irq recv */ + u8 irq_recv; + /** last irq processed */ + u8 irq_done; + /** sdio int status */ + u8 sd_ireg; + /** buf allocated for transmit */ + u8 *tx_buffer; + /** buf for transmit */ + u8 *tx_buf; + /** buf allocated for read interrupt status */ + u8 *hw_regs_buf; + /** buf for read interrupt status */ + u8 *hw_regs; + /** tx pending */ + u32 skb_pending; +/** Version string buffer length */ +#define MAX_VER_STR_LEN 128 + /** Driver version */ + u8 drv_ver[MAX_VER_STR_LEN]; + /** Number of command timeout */ + u32 num_cmd_timeout; +} bt_adapter, *pbt_adapter; + +/** Length of prov name */ +#define PROC_NAME_LEN 32 + +/** Item data structure */ +struct item_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** Size */ + u32 size; + /** Address */ + t_ptr addr; + /** Offset */ + u32 offset; + /** Flag */ + u32 flag; +}; + +/** Proc private data structure */ +struct proc_private_data { + /** Name */ + char name[PROC_NAME_LEN]; + /** File flag */ + u32 fileflag; + /** Buffer size */ + u32 bufsize; + /** Number of items */ + u32 num_items; + /** Item data */ + struct item_data *pdata; + /** Private structure */ + struct _bt_private *pbt; + /** File operations */ + const struct file_operations *fops; +}; + +/** Device proc structure */ +struct device_proc { + /** Proc directory entry */ + struct proc_dir_entry *proc_entry; + /** proc entry for hist */ + struct proc_dir_entry *hist_entry; + /** num of proc files */ + u8 num_proc_files; + /** pointer to proc_private_data */ + struct proc_private_data *pfiles; +}; + +/** Private structure for the MV device */ +typedef struct _bt_private { + /** Bluetooth device */ + bt_dev_t bt_dev; + /** Adapter */ + bt_adapter *adapter; + /** Firmware helper */ + const struct firmware *fw_helper; + /** Firmware */ + const struct firmware *firmware; + /** Init user configure file */ + const struct firmware *init_user_cfg; + /** Init user configure wait queue token */ + u16 init_user_conf_wait_flag; + /** Init user configure file wait queue */ + wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__; + /** Firmware request start time */ + struct timeval req_fw_time; + /** Hotplug device */ + struct device *hotplug_device; + /** thread to service interrupts */ + bt_thread MainThread; + /** proc data */ + struct device_proc dev_proc[MAX_RADIO_FUNC]; + /** Driver lock */ + spinlock_t driver_lock; + /** Driver lock flags */ + ulong driver_flags; + /** Driver reference flags */ + struct kobject kobj; + int debug_device_pending; + int debug_ocf_ogf[2]; + u8 fw_reload; + /* hist_data_len */ + u8 hist_data_len; + /** hist data */ + bt_histogram_data hist_data[MAX_ANTENNA_NUM]; + /** hist proc data */ + bt_hist_proc_data hist_proc[MAX_ANTENNA_NUM]; +#ifdef BLE_WAKEUP + u8 ble_wakeup_buf_size; + u8 *ble_wakeup_buf; +#endif +} bt_private, *pbt_private; + +int bt_get_histogram(bt_private *priv); + +/** Disable interrupt */ +#define OS_INT_DISABLE spin_lock_irqsave(&priv->driver_lock, \ + priv->driver_flags) +/** Enable interrupt */ +#define OS_INT_RESTORE spin_unlock_irqrestore(&priv->driver_lock, \ + priv->driver_flags) + +#ifndef HCI_BT_AMP +/** BT_AMP flag for device type */ +#define HCI_BT_AMP 0x80 +#endif + +/** Device type of BT */ +#define DEV_TYPE_BT 0x00 +/** Device type of AMP */ +#define DEV_TYPE_AMP 0x01 + +/** Marvell vendor packet */ +#define MRVL_VENDOR_PKT 0xFE + +/** Bluetooth command : Get FW Version */ +#define BT_CMD_GET_FW_VERSION 0x0F +/** Bluetooth command : Sleep mode */ +#define BT_CMD_AUTO_SLEEP_MODE 0x23 +/** Bluetooth command : Host Sleep configuration */ +#define BT_CMD_HOST_SLEEP_CONFIG 0x59 +/** Bluetooth command : Host Sleep enable */ +#define BT_CMD_HOST_SLEEP_ENABLE 0x5A +/** Bluetooth command : Module Configuration request */ +#define BT_CMD_MODULE_CFG_REQ 0x5B + +/** Bluetooth command : PMIC Configure */ +#define BT_CMD_PMIC_CONFIGURE 0x7D + +/** Bluetooth command : SDIO pull up down configuration request */ +#define BT_CMD_SDIO_PULL_CFG_REQ 0x69 +/** Bluetooth command : Set Evt Filter Command */ +#define BT_CMD_SET_EVT_FILTER 0x05 +/** Bluetooth command : Enable Write Scan Command */ +#define BT_CMD_ENABLE_WRITE_SCAN 0x1A +/** Bluetooth command : Enable Device under test mode */ +#define BT_CMD_ENABLE_DEVICE_TESTMODE 0x03 +/** Sub Command: Module Bring Up Request */ +#define MODULE_BRINGUP_REQ 0xF1 +/** Sub Command: Module Shut Down Request */ +#define MODULE_SHUTDOWN_REQ 0xF2 +/** Module already up */ +#define MODULE_CFG_RESP_ALREADY_UP 0x0c +/** Sub Command: Host Interface Control Request */ +#define MODULE_INTERFACE_CTRL_REQ 0xF5 + +/** Bluetooth event : Power State */ +#define BT_EVENT_POWER_STATE 0x20 + +/** Bluetooth Power State : Enable */ +#define BT_PS_ENABLE 0x02 +/** Bluetooth Power State : Disable */ +#define BT_PS_DISABLE 0x03 +/** Bluetooth Power State : Sleep */ +#define BT_PS_SLEEP 0x01 +/** Bluetooth Power State : Awake */ +#define BT_PS_AWAKE 0x02 + +/** Vendor OGF */ +#define VENDOR_OGF 0x3F +/** OGF for reset */ +#define RESET_OGF 0x03 +/** Bluetooth command : Reset */ +#define BT_CMD_RESET 0x03 + +/** Host Sleep activated */ +#define HS_ACTIVATED 0x01 +/** Host Sleep deactivated */ +#define HS_DEACTIVATED 0x00 + +/** Power Save sleep */ +#define PS_SLEEP 0x01 +/** Power Save awake */ +#define PS_AWAKE 0x00 + +/** bt header length */ +#define BT_HEADER_LEN 4 + +#ifndef MAX +/** Return maximum of two */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/** This is for firmware specific length */ +#define EXTRA_LEN 36 + +/** Command buffer size for Marvell driver */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Bluetooth Rx packet buffer size for Marvell driver */ +#define MRVDRV_BT_RX_PACKET_BUFFER_SIZE \ + (HCI_MAX_FRAME_SIZE + EXTRA_LEN) + +/** Buffer size to allocate */ +#define ALLOC_BUF_SIZE (((MAX(MRVDRV_BT_RX_PACKET_BUFFER_SIZE, \ + MRVDRV_SIZE_OF_CMD_BUFFER) + SDIO_HEADER_LEN \ + + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) + +/** Request FW timeout in second */ +#define REQUEST_FW_TIMEOUT 30 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** The number of times to try when waiting for downloaded firmware to + become active when multiple interface is present */ +#define MAX_MULTI_INTERFACE_POLL_TRIES 150 + +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** default idle time */ +#define DEFAULT_IDLE_TIME 1000 + +#define BT_CMD_HEADER_SIZE 3 + +/** BT command structure */ +typedef struct _BT_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** Data */ + u8 data[128]; +} __ATTRIB_PACK__ BT_CMD; + +/** BT event structure */ +typedef struct _BT_EVENT { + /** Event Counter */ + u8 EC; + /** Length */ + u8 length; + /** Data */ + u8 data[8]; +} BT_EVENT; + +#if defined(SDIO_SUSPEND_RESUME) +#define DEF_GPIO_GAP 0xffff +#endif + +#ifdef BLE_WAKEUP +#define BD_ADDR_SIZE 6 +/** Vendor specific event */ +#define VENDOR_SPECIFIC_EVENT 0xff +/** system suspend event */ +#define HCI_SYSTEM_SUSPEND_EVT 0x80 +/** system suspend */ +#define HCI_SYSTEM_SUSPEND 0x00 +/** system resume */ +#define HCI_SYSTEM_RESUME 0x01 +/** This function enables ble wake up pattern */ +int bt_config_ble_wakeup(bt_private *priv); +int bt_send_system_event(bt_private *priv, u8 flag); +#endif + +/** This function verify the received event pkt */ +int check_evtpkt(bt_private *priv, struct sk_buff *skb); + +/* Prototype of global function */ +/** This function gets the priv reference */ +struct kobject *bt_priv_get(bt_private *priv); +/** This function release the priv reference */ +void bt_priv_put(bt_private *priv); +/** This function adds the card */ +bt_private *bt_add_card(void *card); +/** This function removes the card */ +int bt_remove_card(void *card); +/** This function handles the interrupt */ +void bt_interrupt(struct m_dev *m_dev); + +/** This function creates proc interface directory structure */ +int bt_root_proc_init(void); +/** This function removes proc interface directory structure */ +int bt_root_proc_remove(void); +/** This function initializes proc entry */ +int bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq); +/** This function removes proc interface */ +void bt_proc_remove(bt_private *priv); + +/** This function process the received event */ +int bt_process_event(bt_private *priv, struct sk_buff *skb); +/** This function enables host sleep */ +int bt_enable_hs(bt_private *priv); +/** This function used to send command to firmware */ +int bt_prepare_command(bt_private *priv); +/** This function frees the structure of adapter */ +void bt_free_adapter(bt_private *priv); +/** This function handle the receive packet */ +void bt_recv_frame(bt_private *priv, struct sk_buff *skb); + +/** clean up m_devs */ +void clean_up_m_devs(bt_private *priv); +/** bt driver call this function to register to bus driver */ +int *sbi_register(void); +/** bt driver call this function to unregister to bus driver */ +void sbi_unregister(void); +/** bt driver calls this function to register the device */ +int sbi_register_dev(bt_private *priv); +/** bt driver calls this function to unregister the device */ +int sbi_unregister_dev(bt_private *priv); +/** This function initializes firmware */ +int sbi_download_fw(bt_private *priv); +/** Configures hardware to quit deep sleep state */ +int sbi_wakeup_firmware(bt_private *priv); +/** Module configuration and register device */ +int sbi_register_conf_dpc(bt_private *priv); + +/** This function is used to send the data/cmd to hardware */ +int sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb); +/** This function reads the current interrupt status register */ +int sbi_get_int_status(bt_private *priv); +/** This function enables the host interrupts */ +int sbi_enable_host_int(bt_private *priv); +/** This function disables the host interrupts */ +int sbi_disable_host_int(bt_private *priv); + +/** bt fw reload flag */ +extern int bt_fw_reload; +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 +/** This function reload firmware */ +void bt_request_fw_reload(bt_private *priv, int mode); +#define MAX_TX_BUF_SIZE 2312 +/** This function downloads firmware image to the card */ +int sd_download_firmware_w_helper(bt_private *priv); +void bt_dump_sdio_regs(bt_private *priv); +/* dumps the firmware to /var/ or /data/ */ +void bt_dump_firmware_info_v2(bt_private *priv); + +/** Max line length allowed in init config file */ +#define MAX_LINE_LEN 256 +/** Max MAC address string length allowed */ +#define MAX_MAC_ADDR_LEN 18 +/** Max register type/offset/value etc. parameter length allowed */ +#define MAX_PARAM_LEN 12 + +/** Bluetooth command : Mac address configuration */ +#define BT_CMD_CONFIG_MAC_ADDR 0x22 +/** Bluetooth command : Write CSU register */ +#define BT_CMD_CSU_WRITE_REG 0x66 +/** Bluetooth command : Load calibrate data */ +#define BT_CMD_LOAD_CONFIG_DATA 0x61 +/** Bluetooth command : Load calibrate ext data */ +#define BT_CMD_LOAD_CONFIG_DATA_EXT 0x60 + +/** Bluetooth command : BLE deepsleep */ +#define BT_CMD_BLE_DEEP_SLEEP 0x8b + +/** BT_BLE command structure */ +typedef struct _BT_BLE_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** deepsleep flag */ + u8 deepsleep; +} __ATTRIB_PACK__ BT_BLE_CMD; + +/** BT_CSU command structure */ +typedef struct _BT_CSU_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** reg type */ + u8 type; + /** address */ + u8 offset[4]; + /** Data */ + u8 value[2]; +} __ATTRIB_PACK__ BT_CSU_CMD; + +/** This function sets mac address */ +int bt_set_mac_address(bt_private *priv, u8 *mac); +/** This function writes value to CSU registers */ +int bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value); +/** BT set user defined init data and param */ +int bt_init_config(bt_private *priv, char *cfg_file); +/** BT PMIC Configure command */ +int bt_pmic_configure(bt_private *priv); +/** This function load the calibrate data */ +int bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac); +/** This function load the calibrate ext data */ +int bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len); +/** BT set user defined calibration data */ +int bt_cal_config(bt_private *priv, char *cfg_file, char *mac); +/** BT set user defined calibration ext data */ +int bt_cal_config_ext(bt_private *priv, char *cfg_file); +int bt_init_mac_address(bt_private *priv, char *mac); + +int bt_set_independent_reset(bt_private *priv); +/** Bluetooth command : Independent reset */ +#define BT_CMD_INDEPENDENT_RESET 0x0D + +/** BT HCI command structure */ +typedef struct _BT_HCI_CMD { + /** OCF OGF */ + u16 ocf_ogf; + /** Length */ + u8 length; + /** cmd type */ + u8 cmd_type; + /** cmd len */ + u8 cmd_len; + /** Data */ + u8 data[6]; +} __ATTRIB_PACK__ BT_HCI_CMD; + +#endif /* _BT_DRV_H_ */
diff --git a/bt_sd8997/bt_char/bt_init.c b/bt_sd8997/bt_char/bt_init.c new file mode 100644 index 0000000..9d18912 --- /dev/null +++ b/bt_sd8997/bt_char/bt_init.c
@@ -0,0 +1,751 @@ +/** @file bt_init.c + * + * @brief This file contains the init functions for BlueTooth + * driver. + * + * Copyright (C) 2011-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/string.h> +#include <linux/firmware.h> + +#include "bt_drv.h" + +extern int bt_req_fw_nowait; + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) + +#define isdigit(c) (('0' <= (c) && (c) <= '9')) +#define isspace(c) (c <= ' ' && (c == ' ' || (c <= 13 && c >= 9))) +/** + * @brief Returns hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +static int +bt_hexval(char chr) +{ + ENTER(); + + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + LEAVE(); + return 0; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +static char * +bt_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Returns hex value of a given ascii string + * + * @param a String to be converted + * + * @return hex value + */ +static int +bt_atox(const char *a) +{ + int i = 0; + ENTER(); + while (isxdigit(*a)) + i = i * 16 + bt_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Converts mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +static void +bt_mac2u8(u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + memcpy(mac_buff, buf, strlen(buf)); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = bt_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = bt_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +/** + * @brief Returns integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_atoi(int *data, char *a) +{ + int i, val = 0, len; + + ENTER(); + + len = strlen(a); + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = bt_atox(a); + return BT_STATUS_SUCCESS; + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + PRINTM(ERROR, "Invalid char %c in string %s\n", a[i], + a); + return BT_STATUS_FAILURE; + } + } + *data = val; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief parse cal-data + * + * @param src a pointer to cal-data string + * @param len len of cal-data + * @param dst a pointer to return cal-data + * @param dst_size size of dest buffer + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 *dst_size) +{ + const u8 *ptr; + u8 *dptr; + u32 count = 0; + int ret = BT_STATUS_FAILURE; + + ENTER(); + ptr = src; + dptr = dst; + + while ((ptr - src) < len) { + if (*ptr && isspace(*ptr)) { + ptr++; + continue; + } + + if (isxdigit(*ptr)) { + if ((dptr - dst) >= *dst_size) { + PRINTM(ERROR, "cal_file size too big!!!\n"); + goto done; + } + *dptr++ = bt_atox((const char *)ptr); + ptr += 2; + count++; + } else { + ptr++; + } + } + if (dptr == dst) { + ret = BT_STATUS_FAILURE; + goto done; + } + + *dst_size = count; + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief BT get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return -1 or length of the line + */ +int +parse_cfg_get_line(u8 *data, u32 size, u8 *line_pos) +{ + static s32 pos; + u8 *src, *dest; + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while (pos < size && *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + *dest = '\0'; + /* parse new line */ + pos++; + return strlen((const char *)line_pos); +} + +/** + * @brief BT parse ASCII format data to MAC address + * + * @param priv BT private handle + * @param data Source data + * @param size data length + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_init_cfg(bt_private *priv, u8 *data, u32 size) +{ + u8 *pos; + u8 *intf_s, *intf_e; + u8 s[MAX_LINE_LEN]; /* 1 line data */ + u32 line_len; + char dev_name[MAX_PARAM_LEN]; + u8 buf[MAX_PARAM_LEN]; + u8 bt_addr[MAX_MAC_ADDR_LEN]; + u8 bt_mac[ETH_ALEN]; + int setting = 0; + u8 type = 0; + u16 value = 0; + u32 offset = 0; + int ret = BT_STATUS_FAILURE; + + memset(dev_name, 0, sizeof(dev_name)); + memset(bt_addr, 0, sizeof(bt_addr)); + memset(bt_mac, 0, sizeof(bt_mac)); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Need n't process this line */ + + /* Process MAC addr */ + if (strncmp((char *)pos, "mac_addr", 8) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(dev_name, (const char *)intf_s + 1, + intf_e - intf_s - 1); + dev_name[intf_e - intf_s - 1] = '\0'; + strncpy((char *)bt_addr, + (const char *)intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bt_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + /* Convert MAC format */ + bt_mac2u8(bt_mac, (char *)bt_addr); + PRINTM(CMD, + "HCI: %s new BT Address " MACSTR "\n", + dev_name, MAC2STR(bt_mac)); + if (BT_STATUS_SUCCESS != + bt_set_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp((char *)pos, "bt_reg", 6) == 0) { + intf_s = (u8 *)strchr((const char *)pos, '='); + if (intf_s != NULL) + intf_e = (u8 *)strchr((const char *)intf_s, + ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s + 1, + 1); + buf[1] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + type = (u8)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg type\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = (u8 *)strchr((const char *)intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(ERROR, + "BT: Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, + intf_e - intf_s); + buf[intf_e - intf_s] = '\0'; + if (0 == bt_atoi(&setting, (char *)buf)) + offset = (u32)setting; + else { + PRINTM(ERROR, + "BT: Fail to parse reg offset\n"); + goto done; + } + } else { + PRINTM(ERROR, + "BT: Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen((const char *)intf_s) >= MAX_PARAM_LEN)) { + PRINTM(ERROR, + "BT: Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + memset(buf, 0, sizeof(buf)); + strncpy((char *)buf, (const char *)intf_s, sizeof(buf)); + if (0 == bt_atoi(&setting, (char *)buf)) + value = (u16) setting; + else { + PRINTM(ERROR, "BT: Fail to parse reg value\n"); + goto done; + } + + PRINTM(CMD, + "BT: Write reg type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + if (BT_STATUS_SUCCESS != + bt_write_reg(priv, type, offset, value)) { + PRINTM(FATAL, + "BT: Write reg failed. type: %d offset: 0x%x value: 0x%x\n", + type, offset, value); + goto done; + } + } + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to bt_private structure + * + * @return N/A + */ +static void +bt_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + bt_private *priv = (bt_private *)context; + + ENTER(); + + if (!firmware) + PRINTM(ERROR, "BT user init config request firmware failed\n"); + + priv->init_user_cfg = firmware; + priv->init_user_conf_wait_flag = TRUE; + wake_up_interruptible(&priv->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief BT set user defined init data and param + * + * @param priv BT private handle + * @param cfg_file user cofig file + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_config(bt_private *priv, char *cfg_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if ((request_firmware(&cfg, cfg_file, priv->hotplug_device)) < 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", cfg_file); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cfg) + ret = bt_process_init_cfg(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg(bt_private *priv, u8 *data, u32 size, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + u8 cal_data[32]; + u8 *mac_data = NULL; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + u8 *pcal_data = cal_data; + + memset(bt_mac, 0, sizeof(bt_mac)); + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (mac != NULL) { + /* Convert MAC format */ + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: new BT Address " MACSTR "\n", + MAC2STR(bt_mac)); + mac_data = bt_mac; + } + if (BT_STATUS_SUCCESS != bt_load_cal_data(priv, pcal_data, mac_data)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT data + * + * @param priv a pointer to bt_private structure + * @param data a pointer to cal data + * @param size cal data size + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_cal_cfg_ext(bt_private *priv, u8 *data, u32 size) +{ + u8 cal_data[128]; + u32 cal_data_len; + int ret = BT_STATUS_FAILURE; + + cal_data_len = sizeof(cal_data); + if (BT_STATUS_SUCCESS != + bt_parse_cal_cfg(data, size, cal_data, &cal_data_len)) { + goto done; + } + if (BT_STATUS_SUCCESS != + bt_load_cal_data_ext(priv, cal_data, cal_data_len)) { + PRINTM(FATAL, "BT: Fail to load calibrate data\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config(bt_private *priv, char *cal_file, char *mac) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size, mac); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT process calibration EXT file + * + * @param priv a pointer to bt_private structure + * @param cal_file calibration file name + * @param mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_cal_config_ext(bt_private *priv, char *cal_file) +{ + const struct firmware *cfg = NULL; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + GFP_KERNEL, priv, + bt_request_init_user_conf_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cal_file, priv->hotplug_device, + priv, + bt_request_init_user_conf_callback); +#endif +#endif + if (ret < 0) { + PRINTM(FATAL, + "BT: bt_cal_config_ext() failed, error code = %#x cal_file=%s\n", + ret, cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->init_user_conf_wait_flag = FALSE; + wait_event_interruptible(priv->init_user_conf_wait_q, + priv->init_user_conf_wait_flag); + cfg = priv->init_user_cfg; + } else { + if ((request_firmware(&cfg, cal_file, priv->hotplug_device)) < + 0) { + PRINTM(FATAL, "BT: request_firmware() %s failed\n", + cal_file); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cfg) + ret = bt_process_cal_cfg_ext(priv, (u8 *)cfg->data, cfg->size); + else + ret = BT_STATUS_FAILURE; +done: + if (cfg) + release_firmware(cfg); + LEAVE(); + return ret; +} + +/** + * @brief BT init mac address from bt_mac parametre when insmod + * + * @param priv a pointer to bt_private structure + * @param bt_mac mac address buf + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_init_mac_address(bt_private *priv, char *mac) +{ + u8 bt_mac[ETH_ALEN]; + int ret = BT_STATUS_FAILURE; + + ENTER(); + memset(bt_mac, 0, sizeof(bt_mac)); + bt_mac2u8(bt_mac, mac); + PRINTM(CMD, "HCI: New BT Address " MACSTR "\n", MAC2STR(bt_mac)); + ret = bt_set_mac_address(priv, bt_mac); + if (ret != BT_STATUS_SUCCESS) + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre.\n"); + + LEAVE(); + return ret; +}
diff --git a/bt_sd8997/bt_char/bt_main.c b/bt_sd8997/bt_char/bt_main.c new file mode 100644 index 0000000..1ac0fc2 --- /dev/null +++ b/bt_sd8997/bt_char/bt_main.c
@@ -0,0 +1,3750 @@ +/** @file bt_main.c + * + * @brief This file contains the major functions in BlueTooth + * driver. It includes init, exit, open, close and main + * thread etc.. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/** + * @mainpage M-BT Linux Driver + * + * @section overview_sec Overview + * + * The M-BT is a Linux reference driver for Marvell Bluetooth chipset. + * + * @section copyright_sec Copyright + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + */ +#include <linux/module.h> + +#ifdef CONFIG_OF +#include <linux/of.h> +#endif + +#include <linux/mmc/sdio_func.h> + +#include "bt_drv.h" +#include "mbt_char.h" +#include "bt_sdio.h" + +/** Version */ +#define VERSION "C3X14113" + +/** Driver version */ +static char mbt_driver_version[] = "SD8997-%s-" VERSION "-(" "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** Declare and initialize fw_version */ +static char fw_version[32] = "0.0.0.p0"; + +#define AID_SYSTEM 1000 /* system server */ + +#define AID_BLUETOOTH 1002 /* bluetooth subsystem */ + +#define AID_NET_BT_STACK 3008 /* bluetooth stack */ + +/** Define module name */ + +#define MODULE_NAME "bt_fm_nfc" + +/** Declaration of chardev class */ +static struct class *chardev_class; + +/** Interface specific variables */ +static int mbtchar_minor; +static int debugchar_minor; + +/** + * The global variable of a pointer to bt_private + * structure variable + **/ +bt_private *m_priv[MAX_BT_ADAPTER]; + +/** Default Driver mode */ +static int drv_mode = (DRV_MODE_BT); + +/** BT interface name */ +static char *bt_name; +/** BT debug interface name */ +static char *debug_name; + +/** fw reload flag */ +int bt_fw_reload; +/** fw serial download flag */ +int bt_fw_serial = 1; + +/** Firmware flag */ +static int fw = 1; +/** default powermode */ +static int psmode = 1; +/** default BLE deep sleep */ +static int deep_sleep = 1; +/** Init config file (MAC address, register etc.) */ +static char *init_cfg; +/** Calibration config file (MAC address, init powe etc.) */ +static char *cal_cfg; +/** Calibration config file EXT */ +static char *cal_cfg_ext; +/** Init MAC address */ +static char *bt_mac; +static int btindrst = -1; + +/** Setting mbt_drvdbg value based on DEBUG level */ +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff & ~DBG_EVENT) +#else +#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) +#endif /* DEBUG_LEVEL2 */ +u32 mbt_drvdbg = DEFAULT_DEBUG_MASK; +#endif + +#ifdef CONFIG_OF +static int dts_enable = 1; +#endif + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int mbt_pm_keep_power = 1; +#endif + +static int debug_intf = 1; + +static int btpmic = 0; + +/** Offset of sequence number in event */ +#define OFFSET_SEQNUM 4 + +/** + * @brief handle received packet + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * + * @return N/A + */ +void +bt_recv_frame(bt_private *priv, struct sk_buff *skb) +{ + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + mdev_recv_frame(skb); + mdev_bt->stat.byte_rx += skb->len; + } + return; +} + +/** + * @brief Alloc bt device + * + * @return pointer to structure mbt_dev or NULL + */ +struct mbt_dev * +alloc_mbt_dev(void) +{ + struct mbt_dev *mbt_dev; + ENTER(); + + mbt_dev = kzalloc(sizeof(struct mbt_dev), GFP_KERNEL); + if (!mbt_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return mbt_dev; +} + +/** + * @brief Alloc debug device + * + * @return pointer to structure debug_level or NULL + */ +struct debug_dev * +alloc_debug_dev(void) +{ + struct debug_dev *debug_dev; + ENTER(); + + debug_dev = kzalloc(sizeof(struct debug_dev), GFP_KERNEL); + if (!debug_dev) { + LEAVE(); + return NULL; + } + + LEAVE(); + return debug_dev; +} + +/** + * @brief Frees m_dev + * + * @return N/A + */ +void +free_m_dev(struct m_dev *m_dev) +{ + ENTER(); + kfree(m_dev->dev_pointer); + m_dev->dev_pointer = NULL; + LEAVE(); +} + +/** + * @brief clean up m_devs + * + * @return N/A + */ +void +clean_up_m_devs(bt_private *priv) +{ + struct m_dev *m_dev = NULL; + int i; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if (m_dev->spec_type == IANYWHERE_SPEC) { + if ((drv_mode & DRV_MODE_BT) && (mbtchar_minor > 0)) + mbtchar_minor--; + m_dev->close(m_dev); + for (i = 0; i < 3; i++) + kfree_skb(((struct mbt_dev *) + (m_dev->dev_pointer))-> + reassembly[i]); + /** unregister m_dev to char_dev */ + if (chardev_class) + chardev_cleanup_one(m_dev, chardev_class); + free_m_dev(m_dev); + } + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = NULL; + } + if (priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer) { + m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + PRINTM(MSG, "BT: Delete %s\n", m_dev->name); + if ((debug_intf) && (debugchar_minor > 0)) + debugchar_minor--; + /** unregister m_dev to char_dev */ + if (chardev_class) + chardev_cleanup_one(m_dev, chardev_class); + free_m_dev(m_dev); + priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = NULL; + } + LEAVE(); + return; +} + +/** + * @brief This function verify the received event pkt + * + * Event format: + * +--------+--------+--------+--------+--------+ + * | Event | Length | ncmd | Opcode | + * +--------+--------+--------+--------+--------+ + * | 1-byte | 1-byte | 1-byte | 2-byte | + * +--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +check_evtpkt(bt_private *priv, struct sk_buff *skb) +{ + struct hci_event_hdr *hdr = (struct hci_event_hdr *)skb->data; + struct hci_ev_cmd_complete *ec; + u16 opcode, ocf; + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (!priv->bt_dev.sendcmdflag) { + ret = BT_STATUS_FAILURE; + goto exit; + } + if (hdr->evt == HCI_EV_CMD_COMPLETE) { + ec = (struct hci_ev_cmd_complete *) + (skb->data + HCI_EVENT_HDR_SIZE); + opcode = __le16_to_cpu(ec->opcode); + ocf = hci_opcode_ocf(opcode); + PRINTM(CMD, + "BT: CMD_COMPLTE opcode=0x%x, ocf=0x%x, send_cmd_opcode=0x%x\n", + opcode, ocf, priv->bt_dev.send_cmd_opcode); + if (opcode != priv->bt_dev.send_cmd_opcode) { + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (ocf) { + case BT_CMD_MODULE_CFG_REQ: + case BT_CMD_BLE_DEEP_SLEEP: + case BT_CMD_CONFIG_MAC_ADDR: + case BT_CMD_CSU_WRITE_REG: + case BT_CMD_LOAD_CONFIG_DATA: + case BT_CMD_LOAD_CONFIG_DATA_EXT: + case BT_CMD_AUTO_SLEEP_MODE: + case BT_CMD_HOST_SLEEP_CONFIG: + case BT_CMD_SDIO_PULL_CFG_REQ: + case BT_CMD_SET_EVT_FILTER: + case BT_CMD_ENABLE_WRITE_SCAN: + // case BT_CMD_ENABLE_DEVICE_TESTMODE: + case BT_CMD_RESET: + case BT_CMD_PMIC_CONFIGURE: + case BT_CMD_INDEPENDENT_RESET: + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter->cmd_wait_q); + break; + case BT_CMD_GET_FW_VERSION: + { + u8 *pos = (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + snprintf(fw_version, sizeof(fw_version), + "%u.%u.%u.p%u", pos[2], pos[1], pos[0], + pos[3]); + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } + case BT_CMD_HISTOGRAM: + { + u8 *status = + skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete); + u8 *pos = + (skb->data + HCI_EVENT_HDR_SIZE + + sizeof(struct hci_ev_cmd_complete) + + 1); + if (*status == 0) { + priv->hist_data_len = + hdr->plen - + sizeof(struct + hci_ev_cmd_complete) - 1; + if (priv->hist_data_len > + sizeof(priv->hist_data)) + priv->hist_data_len = + sizeof(priv->hist_data); + memcpy(priv->hist_data, pos, + priv->hist_data_len); + PRINTM(CMD, "histogram len=%d\n", + priv->hist_data_len); + } + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + break; + } + case BT_CMD_HOST_SLEEP_ENABLE: + priv->bt_dev.sendcmdflag = FALSE; + break; + default: + /** Ignore command not defined but send by driver */ + if (opcode == priv->bt_dev.send_cmd_opcode) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } else { + ret = BT_STATUS_FAILURE; + } + break; + } + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** +* @brief This function stores the FW dumps received from events in a file +* +* @param priv A pointer to bt_private structure +* @param skb A pointer to rx skb +* +* @return N/A +*/ +void +bt_store_firmware_dump(bt_private *priv, u8 *buf, u32 len) +{ + struct file *pfile_fwdump = NULL; + loff_t pos = 0; + u16 seqnum = 0; + struct timeval t; + u32 sec; + + ENTER(); + + seqnum = __le16_to_cpu(*(u16 *) (buf + OFFSET_SEQNUM)); + + if (priv->adapter->fwdump_fname && seqnum != 1) { + pfile_fwdump = + filp_open((const char *)priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file.\n"); + LEAVE(); + return; + } + } else { + if (!priv->adapter->fwdump_fname) { + gfp_t flag; + flag = (in_atomic() || + irqs_disabled())? GFP_ATOMIC : GFP_KERNEL; + priv->adapter->fwdump_fname = kzalloc(64, flag); + } else + memset(priv->adapter->fwdump_fname, 0, 64); + + do_gettimeofday(&t); + sec = (u32)t.tv_sec; + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/var/log/bt_fwdump_", sec); + pfile_fwdump = + filp_open(priv->adapter->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + sprintf(priv->adapter->fwdump_fname, "%s%u", + "/data/bt_fwdump_", sec); + pfile_fwdump = + filp_open((const char *)priv->adapter-> + fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + } + } + + if (IS_ERR(pfile_fwdump)) { + PRINTM(MSG, "Cannot create firmware dump file\n"); + LEAVE(); + return; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_fwdump, buf, len, &pos); +#else + kernel_write(pfile_fwdump, buf, len, &pos); +#endif + filp_close(pfile_fwdump, NULL); + LEAVE(); + return; +} + +/** + * @brief This function process the received event + * + * Event format: + * +--------+--------+--------+--------+-----+ + * | EC | Length | Data | + * +--------+--------+--------+--------+-----+ + * | 1-byte | 1-byte | n-byte | + * +--------+--------+--------+--------+-----+ + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to rx skb + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_process_event(bt_private *priv, struct sk_buff *skb) +{ + int ret = BT_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + BT_EVENT *pevent; + + ENTER(); + if (!m_dev) { + PRINTM(CMD, "BT: bt_process_event without m_dev\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pevent = (BT_EVENT *)skb->data; + if (pevent->EC != 0xff) { + PRINTM(CMD, "BT: Not Marvell Event=0x%x\n", pevent->EC); + ret = BT_STATUS_FAILURE; + goto exit; + } + switch (pevent->data[0]) { + case BT_CMD_HISTOGRAM: + break; + case BT_CMD_AUTO_SLEEP_MODE: + if (pevent->data[2] == BT_STATUS_SUCCESS) { + if (pevent->data[1] == BT_PS_ENABLE) + priv->adapter->psmode = 1; + else + priv->adapter->psmode = 0; + PRINTM(CMD, "BT: PS Mode %s:%s\n", m_dev->name, + (priv->adapter->psmode) ? "Enable" : "Disable"); + + } else { + PRINTM(CMD, "BT: PS Mode Command Fail %s\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_CONFIG: + if (pevent->data[3] == BT_STATUS_SUCCESS) { + PRINTM(CMD, "BT: %s: gpio=0x%x, gap=0x%x\n", + m_dev->name, pevent->data[1], pevent->data[2]); + } else { + PRINTM(CMD, "BT: %s: HSCFG Command Fail\n", + m_dev->name); + } + break; + case BT_CMD_HOST_SLEEP_ENABLE: + if (pevent->data[1] == BT_STATUS_SUCCESS) { + priv->adapter->hs_state = HS_ACTIVATED; + if (priv->adapter->suspend_fail == FALSE) { +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED + bt_is_suspended(priv); +#endif +#endif +#endif + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + if (priv->adapter->psmode) + priv->adapter->ps_state = PS_SLEEP; + PRINTM(CMD, "BT: EVENT %s: HS ACTIVATED!\n", + m_dev->name); + + } else { + PRINTM(CMD, "BT: %s: HS Enable Fail\n", m_dev->name); + } + break; + case BT_CMD_MODULE_CFG_REQ: + if ((priv->bt_dev.sendcmdflag == TRUE) && + ((pevent->data[1] == MODULE_BRINGUP_REQ) + || (pevent->data[1] == MODULE_SHUTDOWN_REQ))) { + if (pevent->data[1] == MODULE_BRINGUP_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2] && (pevent->data[2] != + MODULE_CFG_RESP_ALREADY_UP)) + ? "Bring up Fail" : "Bring up success"); + priv->bt_dev.devType = pevent->data[3]; + PRINTM(CMD, "devType:%s\n", + (pevent->data[3] == + DEV_TYPE_AMP) ? "AMP controller" : + "BR/EDR controller"); + priv->bt_dev.devFeature = pevent->data[4]; + PRINTM(CMD, "devFeature: %s, %s, %s" + "\n", + ((pevent-> + data[4] & DEV_FEATURE_BT) ? + "BT Feature" : "No BT Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BTAMP) ? + "BTAMP Feature" : "No BTAMP Feature"), + ((pevent-> + data[4] & DEV_FEATURE_BLE) ? + "BLE Feature" : "No BLE Feature") + ); + } + if (pevent->data[1] == MODULE_SHUTDOWN_REQ) { + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (pevent->data[2]) ? "Shut down Fail" + : "Shut down success"); + + } + if (pevent->data[2]) { + priv->bt_dev.sendcmdflag = FALSE; + priv->adapter->cmd_complete = TRUE; + wake_up_interruptible(&priv->adapter-> + cmd_wait_q); + } + } else { + PRINTM(CMD, "BT_CMD_MODULE_CFG_REQ resp for APP\n"); + ret = BT_STATUS_FAILURE; + } + break; + case BT_EVENT_POWER_STATE: + if (pevent->data[1] == BT_PS_SLEEP) + priv->adapter->ps_state = PS_SLEEP; + PRINTM(CMD, "BT: EVENT %s:%s\n", m_dev->name, + (priv->adapter->ps_state) ? "PS_SLEEP" : "PS_AWAKE"); + + break; + case BT_CMD_SDIO_PULL_CFG_REQ: + if (pevent->data[pevent->length - 1] == BT_STATUS_SUCCESS) + PRINTM(CMD, "BT: %s: SDIO pull configuration success\n", + m_dev->name); + + else { + PRINTM(CMD, "BT: %s: SDIO pull configuration fail\n", + m_dev->name); + + } + break; + default: + PRINTM(CMD, "BT: Unknown Event=%d %s\n", pevent->data[0], + m_dev->name); + ret = BT_STATUS_FAILURE; + break; + } +exit: + if (ret == BT_STATUS_SUCCESS) + kfree_skb(skb); + LEAVE(); + return ret; +} + +/** + * @brief This function save the dump info to file + * + * @param dir_name directory name + * @param file_name file_name + * @param buf buffer + * @param buf_len buffer length + * + * @return 0 --success otherwise fail + */ +int +bt_save_dump_info_to_file(char *dir_name, char *file_name, u8 *buf, u32 buf_len) +{ + int ret = BT_STATUS_SUCCESS; + struct file *pfile = NULL; + u8 name[64]; + loff_t pos; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(ERROR, "Can't save dump info to file\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + sprintf((char *)name, "%s/%s", dir_name, file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + sprintf((char *)name, "%s/%s", "/var", file_name); + pfile = filp_open((const char *)name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(ERROR, "Create Dump file for %s error\n", name); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Dump data %s saved in %s\n", file_name, name); + + pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, (const char __user *)buf, buf_len, &pos); + set_fs(fs); +#else + kernel_write(pfile, (const char __user *)buf, buf_len, &pos); +#endif + filp_close(pfile, NULL); + + PRINTM(MSG, "Dump data %s saved in %s successfully\n", file_name, name); + +done: + LEAVE(); + return ret; +} + +#define DEBUG_HOST_READY 0xEE +#define DEBUG_FW_DONE 0xFF +#define DUMP_MAX_POLL_TRIES 200 + +#define DEBUG_DUMP_CTRL_REG 0xF0 +#define DEBUG_DUMP_START_REG 0xF1 +#define DEBUG_DUMP_END_REG 0xF8 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_APU_REGS = 3, + DUMP_TYPE_CIU_REGS = 4, + DUMP_TYPE_ICU_REGS = 5, + DUMP_TYPE_MAC_REGS = 6, + DUMP_TYPE_EXTEND_7 = 7, + DUMP_TYPE_EXTEND_8 = 8, + DUMP_TYPE_EXTEND_9 = 9, + DUMP_TYPE_EXTEND_10 = 10, + DUMP_TYPE_EXTEND_11 = 11, + DUMP_TYPE_EXTEND_12 = 12, + DUMP_TYPE_EXTEND_13 = 13, + DUMP_TYPE_EXTEND_LAST = 14 +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 + +/** Memory type mapping structure */ +typedef struct { + /** memory name */ + u8 mem_name[MAX_NAME_LEN]; + /** memory pointer */ + u8 *mem_Ptr; + /** file structure */ + struct file *pfile_mem; + /** donbe flag */ + u8 done_flag; +} memory_type_mapping; + +memory_type_mapping bt_mem_type_mapping_tbl[] = { + {"ITCM", NULL, NULL, 0xF0}, + {"DTCM", NULL, NULL, 0xF1}, + {"SQRAM", NULL, NULL, 0xF2}, + {"APU", NULL, NULL, 0xF3}, + {"CIU", NULL, NULL, 0xF4}, + {"ICU", NULL, NULL, 0xF5}, + {"MAC", NULL, NULL, 0xF6}, + {"EXT7", NULL, NULL, 0xF7}, + {"EXT8", NULL, NULL, 0xF8}, + {"EXT9", NULL, NULL, 0xF9}, + {"EXT10", NULL, NULL, 0xFA}, + {"EXT11", NULL, NULL, 0xFB}, + {"EXT12", NULL, NULL, 0xFC}, + {"EXT13", NULL, NULL, 0xFD}, + {"EXTLAST", NULL, NULL, 0xFE}, +}; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +/** + * @brief This function read/write firmware via cmd52 + * + * @param phandle A pointer to moal_handle + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status +bt_cmd52_rdwr_firmware(bt_private *priv, u8 doneflag) +{ + int ret = 0; + int tries = 0; + u8 ctrl_data = 0; + u8 dbg_dump_ctrl_reg = 0; + + dbg_dump_ctrl_reg = DEBUG_DUMP_CTRL_REG; + + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + DEBUG_HOST_READY, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < DUMP_MAX_POLL_TRIES; tries++) { + ctrl_data = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)-> + func, dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != DEBUG_HOST_READY) { + PRINTM(INFO, + "The ctrl reg was changed, re-try again!\n"); + sdio_writeb(((struct sdio_mmc_card *)priv->bt_dev. + card)->func, DEBUG_HOST_READY, + dbg_dump_ctrl_reg, &ret); + if (ret) { + PRINTM(ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } + udelay(100); + } + if (ctrl_data == DEBUG_HOST_READY) { + PRINTM(ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void +bt_dump_firmware_info_v2(bt_private *priv) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr = NULL; + u8 dump_num = 0; + u8 idx = 0; + u8 doneflag = 0; + rdwr_status stat; + u8 i = 0; + u8 read_reg = 0; + u32 memory_size = 0; + u8 path_name[64], file_name[32]; + u8 *end_ptr = NULL; + u8 dbg_dump_start_reg = 0; + u8 dbg_dump_end_reg = 0; + + if (!priv) { + PRINTM(ERROR, "Could not dump firmwware info\n"); + return; + } + + memset(path_name, 0, sizeof(path_name)); + strcpy((char *)path_name, "/data"); + PRINTM(MSG, "Create DUMP directory success:dir_name=%s\n", path_name); + + dbg_dump_start_reg = DEBUG_DUMP_START_REG; + dbg_dump_end_reg = DEBUG_DUMP_END_REG; + + sbi_wakeup_firmware(priv); + sdio_claim_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + /* start dump fw memory */ + PRINTM(MSG, "==== DEBUG MODE OUTPUT START ====\n"); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + reg = dbg_dump_start_reg; + dump_num = + sdio_readb(((struct sdio_mmc_card *)priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ MEM NUM ERR\n"); + goto done; + } + + /* read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + if (RDWR_STATUS_FAILURE == + bt_cmd52_rdwr_firmware(priv, doneflag)) + goto done; + memory_size = 0; + reg = dbg_dump_start_reg; + for (i = 0; i < 4; i++) { + read_reg = + sdio_readb(((struct sdio_mmc_card *)priv-> + bt_dev.card)->func, reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + if (memory_size == 0) { + PRINTM(MSG, "Firmware Dump Finished!\n"); + break; + } else { + PRINTM(MSG, "%s_SIZE=0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + memory_size); + bt_mem_type_mapping_tbl[idx].mem_Ptr = + vmalloc(memory_size + 1); + if ((ret != BT_STATUS_SUCCESS) || + !bt_mem_type_mapping_tbl[idx].mem_Ptr) { + PRINTM(ERROR, + "Error: vmalloc %s buffer failed!!!\n", + bt_mem_type_mapping_tbl[idx].mem_name); + goto done; + } + dbg_ptr = bt_mem_type_mapping_tbl[idx].mem_Ptr; + end_ptr = dbg_ptr + memory_size; + } + doneflag = bt_mem_type_mapping_tbl[idx].done_flag; + PRINTM(MSG, "Start %s output, please wait...\n", + bt_mem_type_mapping_tbl[idx].mem_name); + do { + stat = bt_cmd52_rdwr_firmware(priv, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = + sdio_readb(((struct sdio_mmc_card *) + priv->bt_dev.card)->func, + reg, &ret); + if (ret) { + PRINTM(MSG, "SDIO READ ERR\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + PRINTM(MSG, + "pre-allocced buf is not enough\n"); + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MSG, "%s done:" + "size = 0x%x\n", + bt_mem_type_mapping_tbl[idx].mem_name, + (unsigned int)(dbg_ptr - + bt_mem_type_mapping_tbl + [idx].mem_Ptr)); + memset(file_name, 0, sizeof(file_name)); + sprintf((char *)file_name, "%s%s", "file_bt_", + bt_mem_type_mapping_tbl[idx].mem_name); + if (BT_STATUS_SUCCESS != + bt_save_dump_info_to_file((char *)path_name, + (char *)file_name, + bt_mem_type_mapping_tbl + [idx].mem_Ptr, + memory_size)) + PRINTM(MSG, + "Can't save dump file %s in %s\n", + file_name, path_name); + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + break; + } + } while (1); + } + PRINTM(MSG, "==== DEBUG MODE OUTPUT END ====\n"); + /* end dump fw memory */ +done: + sdio_release_host(((struct sdio_mmc_card *)priv->bt_dev.card)->func); + for (idx = 0; idx < dump_num; idx++) { + if (bt_mem_type_mapping_tbl[idx].mem_Ptr) { + vfree(bt_mem_type_mapping_tbl[idx].mem_Ptr); + bt_mem_type_mapping_tbl[idx].mem_Ptr = NULL; + } + } + PRINTM(MSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function shows debug info for timeout of command sending. + * + * @param adapter A pointer to bt_private + * @param cmd Timeout command id + * + * @return N/A + */ +static void +bt_cmd_timeout_func(bt_private *priv, u16 cmd) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + + adapter->num_cmd_timeout++; + + PRINTM(ERROR, "Version = %s\n", adapter->drv_ver); + PRINTM(ERROR, "Timeout Command id = 0x%x\n", cmd); + PRINTM(ERROR, "Number of command timeout = %d\n", + adapter->num_cmd_timeout); + PRINTM(ERROR, "Interrupt counter = %d\n", adapter->IntCounter); + PRINTM(ERROR, "Power Save mode = %d\n", adapter->psmode); + PRINTM(ERROR, "Power Save state = %d\n", adapter->ps_state); + PRINTM(ERROR, "Host Sleep state = %d\n", adapter->hs_state); + PRINTM(ERROR, "hs skip count = %d\n", adapter->hs_skip); + PRINTM(ERROR, "suspend_fail flag = %d\n", adapter->suspend_fail); + PRINTM(ERROR, "suspended flag = %d\n", adapter->is_suspended); + PRINTM(ERROR, "Number of wakeup tries = %d\n", adapter->WakeupTries); + PRINTM(ERROR, "Host Cmd complet state = %d\n", adapter->cmd_complete); + PRINTM(ERROR, "Last irq recv = %d\n", adapter->irq_recv); + PRINTM(ERROR, "Last irq processed = %d\n", adapter->irq_done); + PRINTM(ERROR, "tx pending = %d\n", adapter->skb_pending); + PRINTM(ERROR, "sdio int status = %d\n", adapter->sd_ireg); + bt_dump_sdio_regs(priv); + LEAVE(); +} + +/** + * @brief This function queue frame + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to sk_buff structure + * + * @return N/A + */ +static void +bt_queue_frame(bt_private *priv, struct sk_buff *skb) +{ + skb_queue_tail(&priv->adapter->tx_queue, skb); +} + +/** + * @brief This function send reset cmd to firmware + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_reset_command(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((RESET_OGF << 10) | BT_CMD_RESET); + pcmd->length = 0x00; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, 3); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue Reset Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Reset timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_RESET); + } else { + PRINTM(CMD, "BT: Reset Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends module cfg cmd to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @param subcmd sub command + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_module_cfg_cmd(bt_private *priv, int subcmd) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_MODULE_CFG_REQ); + pcmd->length = 1; + pcmd->data[0] = subcmd; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "Queue module cfg Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + /* + On some Android platforms certain delay is needed for HCI daemon to + remove this module and close itself gracefully. Otherwise it hangs. + This 10ms delay is a workaround for such platforms as the root cause + has not been found yet. */ + mdelay(10); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: module_cfg_cmd(%#x): timeout sendcmdflag=%d\n", + subcmd, priv->bt_dev.sendcmdflag); + bt_cmd_timeout_func(priv, BT_CMD_MODULE_CFG_REQ); + } else { + PRINTM(CMD, "BT: module cfg Command done\n"); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function to get histogram + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_get_histogram(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HISTOGRAM); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Histogram cmd(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + priv->hist_data_len = 0; + memset(priv->hist_data, 0, sizeof(priv->hist_data)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: histogram timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HISTOGRAM); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables power save mode + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_ps(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_AUTO_SLEEP_MODE); + if (priv->bt_dev.psmode) + pcmd->data[0] = BT_PS_ENABLE; + else + pcmd->data[0] = BT_PS_DISABLE; + if (priv->bt_dev.idle_timeout) { + pcmd->length = 3; + pcmd->data[1] = (u8)(priv->bt_dev.idle_timeout & 0x00ff); + pcmd->data[2] = (priv->bt_dev.idle_timeout & 0xff00) >> 8; + } else { + pcmd->length = 1; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PSMODE Command(0x%x):%d\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: psmode timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_AUTO_SLEEP_MODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends hscfg command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_hscfg_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_CONFIG); + pcmd->length = 2; + pcmd->data[0] = (priv->bt_dev.gpio_gap & 0xff00) >> 8; + pcmd->data[1] = (u8)(priv->bt_dev.gpio_gap & 0x00ff); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue HSCFG Command(0x%x),gpio=0x%x,gap=0x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[0], pcmd->data[1]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: HSCFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_CONFIG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends sdio pull ctrl command + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_send_sdio_pull_ctrl_cmd(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_SDIO_PULL_CFG_REQ); + pcmd->length = 4; + pcmd->data[0] = (priv->bt_dev.sdio_pull_cfg & 0x000000ff); + pcmd->data[1] = (priv->bt_dev.sdio_pull_cfg & 0x0000ff00) >> 8; + pcmd->data[2] = (priv->bt_dev.sdio_pull_cfg & 0x00ff0000) >> 16; + pcmd->data[3] = (priv->bt_dev.sdio_pull_cfg & 0xff000000) >> 24; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, + "Queue SDIO PULL CFG Command(0x%x), PullUp=0x%x%x,PullDown=0x%x%x\n", + __le16_to_cpu(pcmd->ocf_ogf), pcmd->data[1], pcmd->data[0], + pcmd->data[3], pcmd->data[2]); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: SDIO PULL CFG timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_SDIO_PULL_CFG_REQ); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sends command to configure PMIC + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_pmic_configure(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_PMIC_CONFIGURE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue PMIC Configure Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: PMIC Configure timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_PMIC_CONFIGURE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables host sleep + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_hs(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + priv->adapter->suspend_fail = FALSE; + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_HOST_SLEEP_ENABLE); + pcmd->length = 0; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + PRINTM(CMD, "Queue hs enable Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->hs_state, + WAIT_UNTIL_HS_STATE_CHANGED)) { + PRINTM(MSG, "BT: Enable host sleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_HOST_SLEEP_ENABLE); + } + OS_INT_DISABLE; + if ((priv->adapter->hs_state == HS_ACTIVATED) || + (priv->adapter->is_suspended == TRUE)) { + OS_INT_RESTORE; + PRINTM(MSG, "BT: suspend success! skip=%d\n", + priv->adapter->hs_skip); + } else { + priv->adapter->suspend_fail = TRUE; + OS_INT_RESTORE; + priv->adapter->hs_skip++; + ret = BT_STATUS_FAILURE; + PRINTM(MSG, + "BT: suspend skipped! " + "state=%d skip=%d ps_state= %d WakeupTries=%d\n", + priv->adapter->hs_state, priv->adapter->hs_skip, + priv->adapter->ps_state, priv->adapter->WakeupTries); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Set Evt Filter + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_evt_filter(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_SET_EVT_FILTER); + pcmd->length = 0x03; + pcmd->data[0] = 0x02; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Set Evt Filter Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set Evt Filter timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_SET_EVT_FILTER); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Write Scan - Page and Inquiry + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_write_scan(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = __cpu_to_le16((0x03 << 10) | BT_CMD_ENABLE_WRITE_SCAN); + pcmd->length = 0x01; + pcmd->data[0] = 0x03; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue Enable Write Scan Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Write Scan timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_WRITE_SCAN); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function Enable Device under test mode + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_device_under_testmode(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((0x06 << 10) | BT_CMD_ENABLE_DEVICE_TESTMODE); + pcmd->length = 0x00; + bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + PRINTM(CMD, "Queue enable device under testmode Command(0x%x)\n", + __le16_to_cpu(pcmd->ocf_ogf)); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Enable Device under TEST mode timeout\n"); + bt_cmd_timeout_func(priv, BT_CMD_ENABLE_DEVICE_TESTMODE); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function enables test mode and send cmd + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_enable_test_mode(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + /** Set Evt Filter Command */ + ret = bt_set_evt_filter(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Set Evt filter fail\n"); + goto exit; + } + + /** Enable Write Scan Command */ + ret = bt_enable_write_scan(priv); + if (ret != BT_STATUS_SUCCESS) { + PRINTM(ERROR, "BT test_mode: Enable Write Scan fail\n"); + goto exit; + } + + /** Enable Device under test mode */ + ret = bt_enable_device_under_testmode(priv); + if (ret != BT_STATUS_SUCCESS) + PRINTM(ERROR, + "BT test_mode: Enable device under testmode fail\n"); + +exit: + LEAVE(); + return ret; +} + +#define DISABLE_RESET 0x0 +#define ENABLE_OUTBAND_RESET 0x1 +#define ENABLE_INBAND_RESET 0x02 +#define DEFAULT_GPIO 0xff +/** + * @brief This function set GPIO pin + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_independent_reset(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + u8 mode, gpio; + BT_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_INDEPENDENT_RESET); + mode = btindrst & 0xff; + gpio = (btindrst & 0xff00) >> 8; + if (mode == ENABLE_OUTBAND_RESET) { + pcmd->data[0] = ENABLE_OUTBAND_RESET; + if (!gpio) + pcmd->data[1] = DEFAULT_GPIO; + else + pcmd->data[1] = gpio; + } else if (mode == ENABLE_INBAND_RESET) { + pcmd->data[0] = ENABLE_INBAND_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else if (mode == DISABLE_RESET) { + pcmd->data[0] = DISABLE_RESET; + pcmd->data[1] = DEFAULT_GPIO; + } else { + PRINTM(WARN, "Unsupport mode\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + PRINTM(CMD, "BT: independant reset mode=%d gpio=%d\n", mode, gpio); + pcmd->length = 2; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Independent reset : timeout!\n"); + bt_cmd_timeout_func(priv, BT_CMD_INDEPENDENT_RESET); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets ble deepsleep mode + * + * @param priv A pointer to bt_private structure + * @param mode TRUE/FALSE + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_ble_deepsleep(bt_private *priv, int mode) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_BLE_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_BLE_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_BLE_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_BLE_DEEP_SLEEP); + pcmd->length = 1; + pcmd->deepsleep = mode; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_BLE_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set BLE deepsleep = %d (0x%x)\n", mode, + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set BLE deepsleep timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_BLE_DEEP_SLEEP); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function gets FW version + * + * @param priv A pointer to bt_private structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_get_fw_version(bt_private *priv) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_GET_FW_VERSION); + pcmd->length = 0x01; + pcmd->cmd_type = 0x00; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, 4); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout(priv->adapter->cmd_wait_q, + priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Get FW version: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_GET_FW_VERSION); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function sets mac address + * + * @param priv A pointer to bt_private structure + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_set_mac_address(bt_private *priv, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_HCI_CMD *pcmd; + int i = 0; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_HCI_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_HCI_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CONFIG_MAC_ADDR); + pcmd->length = 8; + pcmd->cmd_type = MRVL_VENDOR_PKT; + pcmd->cmd_len = 6; + for (i = 0; i < 6; i++) + pcmd->data[i] = mac[5 - i]; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_HCI_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set mac addr " MACSTR " (0x%x)\n", MAC2STR(mac), + __le16_to_cpu(pcmd->ocf_ogf)); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(MSG, "BT: Set mac addr: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CONFIG_MAC_ADDR); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data(bt_private *priv, u8 *config_data, u8 *mac) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + int i = 0; + /* u8 config_data[28] = {0x37 0x01 0x1c 0x00 0xFF 0xFF 0xFF 0xFF 0x01 + 0x7f 0x04 0x02 0x00 0x00 0xBA 0xCE 0xC0 0xC6 0x2D 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0xF0}; */ + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA); + pcmd->length = 0x20; + pcmd->data[0] = 0x00; + pcmd->data[1] = 0x00; + pcmd->data[2] = 0x00; + pcmd->data[3] = 0x1C; + /* swip cal-data byte */ + for (i = 4; i < 32; i++) + pcmd->data[i] = *(config_data + ((i / 4) * 8 - 1 - i)); + if (mac != NULL) { + pcmd->data[2] = 0x01; /* skip checksum */ + for (i = 24; i < 30; i++) + pcmd->data[i] = mac[29 - i]; + } + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate data: ", pcmd->data, 32); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function load the calibrate EXT data + * + * @param priv A pointer to bt_private structure + * @param config_data A pointer to calibrate data + * @param mac A pointer to mac address + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_load_cal_data_ext(bt_private *priv, u8 *config_data, u32 cfg_data_len) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CMD *pcmd; + + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_LOAD_CONFIG_DATA_EXT); + pcmd->length = cfg_data_len; + + memcpy(pcmd->data, config_data, cfg_data_len); + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, BT_CMD_HEADER_SIZE + pcmd->length); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + + DBG_HEXDUMP(DAT_D, "calirate ext data", pcmd->data, pcmd->length); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Load calibrate ext data: timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_LOAD_CONFIG_DATA_EXT); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function writes value to CSU registers + * + * @param priv A pointer to bt_private structure + * @param type reg type + * @param offset register address + * @param value register value to write + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_write_reg(bt_private *priv, u8 type, u32 offset, u16 value) +{ + struct sk_buff *skb = NULL; + int ret = BT_STATUS_SUCCESS; + BT_CSU_CMD *pcmd; + ENTER(); + skb = bt_skb_alloc(sizeof(BT_CSU_CMD), GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "No free skb\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + pcmd = (BT_CSU_CMD *)skb->data; + pcmd->ocf_ogf = + __cpu_to_le16((VENDOR_OGF << 10) | BT_CMD_CSU_WRITE_REG); + pcmd->length = 7; + pcmd->type = type; + pcmd->offset[0] = (offset & 0x000000ff); + pcmd->offset[1] = (offset & 0x0000ff00) >> 8; + pcmd->offset[2] = (offset & 0x00ff0000) >> 16; + pcmd->offset[3] = (offset & 0xff000000) >> 24; + pcmd->value[0] = (value & 0x00ff); + pcmd->value[1] = (value & 0xff00) >> 8; + bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + skb_put(skb, sizeof(BT_CSU_CMD)); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = __le16_to_cpu(pcmd->ocf_ogf); + priv->adapter->cmd_complete = FALSE; + PRINTM(CMD, "BT: Set CSU reg type=%d reg=0x%x value=0x%x\n", + type, offset, value); + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, "BT: Set CSU reg timeout:\n"); + bt_cmd_timeout_func(priv, BT_CMD_CSU_WRITE_REG); + } +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function used to restore tx_queue + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_restore_tx_queue(bt_private *priv) +{ + struct sk_buff *skb = NULL; + while (!skb_queue_empty(&priv->adapter->pending_queue)) { + skb = skb_dequeue(&priv->adapter->pending_queue); + if (skb) + bt_queue_frame(priv, skb); + } + wake_up_interruptible(&priv->MainThread.waitQ); +} + +/** + * @brief This function used to send command to firmware + * + * Command format: + * +--------+--------+--------+--------+--------+--------+--------+ + * | OCF OGF | Length | Data | + * +--------+--------+--------+--------+--------+--------+--------+ + * | 2-byte | 1-byte | 4-byte | + * +--------+--------+--------+--------+--------+--------+--------+ + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_prepare_command(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (priv->bt_dev.hscfgcmd) { + priv->bt_dev.hscfgcmd = 0; + ret = bt_send_hscfg_cmd(priv); + } + if (priv->bt_dev.pscmd) { + priv->bt_dev.pscmd = 0; + ret = bt_enable_ps(priv); + } + if (priv->bt_dev.sdio_pull_ctrl) { + priv->bt_dev.sdio_pull_ctrl = 0; + ret = bt_send_sdio_pull_ctrl_cmd(priv); + } + if (priv->bt_dev.hscmd) { + priv->bt_dev.hscmd = 0; + if (priv->bt_dev.hsmode) + ret = bt_enable_hs(priv); + else { + ret = sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + } + } + if (priv->bt_dev.test_mode) { + priv->bt_dev.test_mode = 0; + ret = bt_enable_test_mode(priv); + } + LEAVE(); + return ret; +} + +/** @brief This function processes a single packet + * + * @param priv A pointer to bt_private structure + * @param skb A pointer to skb which includes TX packet + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +send_single_packet(bt_private *priv, struct sk_buff *skb) +{ + int ret; + struct sk_buff *new_skb = NULL; + ENTER(); + if (!skb || !skb->data) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + if (!skb->len || ((skb->len + BT_HEADER_LEN) > BT_UPLD_SIZE)) { + PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, + BT_UPLD_SIZE); + LEAVE(); + return BT_STATUS_FAILURE; + } + if (skb_headroom(skb) < BT_HEADER_LEN) { + new_skb = skb_realloc_headroom(skb, BT_HEADER_LEN); + if (!new_skb) { + PRINTM(ERROR, "TX error: realloc_headroom failed %d\n", + BT_HEADER_LEN); + kfree_skb(skb); + LEAVE(); + return BT_STATUS_FAILURE; + } else { + if (new_skb != skb) + dev_kfree_skb_any(skb); + skb = new_skb; + } + } + /* This is SDIO/PCIE specific header length: byte[3][2][1], * type: + byte[0] (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) + */ + skb_push(skb, BT_HEADER_LEN); + skb->data[0] = (skb->len & 0x0000ff); + skb->data[1] = (skb->len & 0x00ff00) >> 8; + skb->data[2] = (skb->len & 0xff0000) >> 16; + skb->data[3] = bt_cb(skb)->pkt_type; + if (bt_cb(skb)->pkt_type == MRVL_VENDOR_PKT) + PRINTM(CMD, "DNLD_CMD: ocf_ogf=0x%x len=%d\n", + __le16_to_cpu(*((u16 *) & skb->data[4])), skb->len); + ret = sbi_host_to_card(priv, skb->data, skb->len); + if (ret == BT_STATUS_FAILURE) + ((struct m_dev *)skb->dev)->stat.err_tx++; + else + ((struct m_dev *)skb->dev)->stat.byte_tx += skb->len; + if (ret != BT_STATUS_PENDING) + kfree_skb(skb); + LEAVE(); + return ret; +} + +#ifdef CONFIG_OF +/** + * @brief This function read the initial parameter from device tress + * + * + * @return N/A + */ +static void +bt_init_from_dev_tree(void) +{ + struct device_node *dt_node = NULL; + struct property *prop; + u32 data; + const char *string_data; + + ENTER(); + + if (!dts_enable) { + PRINTM(CMD, "DTS is disabled!"); + return; + } + + dt_node = of_find_node_by_name(NULL, "sd8xxx-bt"); + if (!dt_node) { + LEAVE(); + return; + } + for_each_property_of_node(dt_node, prop) { +#ifdef DEBUG_LEVEL1 + if (!strncmp(prop->name, "mbt_drvdbg", strlen("mbt_drvdbg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(CMD, "mbt_drvdbg=0x%x\n", data); + mbt_drvdbg = data; + } + } +#endif + else if (!strncmp(prop->name, "init_cfg", strlen("init_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + init_cfg = (char *)string_data; + PRINTM(CMD, "init_cfg=%s\n", init_cfg); + } + } else if (!strncmp + (prop->name, "cal_cfg_ext", strlen("cal_cfg_ext"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg_ext = (char *)string_data; + PRINTM(CMD, "cal_cfg_ext=%s\n", cal_cfg_ext); + } + } else if (!strncmp(prop->name, "cal_cfg", strlen("cal_cfg"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + cal_cfg = (char *)string_data; + PRINTM(CMD, "cal_cfg=%s\n", cal_cfg); + } + } else if (!strncmp(prop->name, "bt_mac", strlen("bt_mac"))) { + if (!of_property_read_string + (dt_node, prop->name, &string_data)) { + bt_mac = (char *)string_data; + PRINTM(CMD, "bt_mac=%s\n", bt_mac); + } + } else if (!strncmp(prop->name, "btindrst", strlen("btindrst"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btindrst = data; + PRINTM(CMD, "btindrst=%d\n", btindrst); + } + } else if (!strncmp(prop->name, "btpmic", strlen("btpmic"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + btpmic = data; + PRINTM(CMD, "btpmic=%d\n", btpmic); + } + } + } + LEAVE(); + return; +} +#endif + +/** + * @brief This function initializes the adapter structure + * and set default value to the member of adapter. + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +static void +bt_init_adapter(bt_private *priv) +{ + ENTER(); +#ifdef CONFIG_OF + bt_init_from_dev_tree(); +#endif + skb_queue_head_init(&priv->adapter->tx_queue); + skb_queue_head_init(&priv->adapter->fwdump_queue); + + skb_queue_head_init(&priv->adapter->pending_queue); + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + priv->adapter->fwdump_fname = NULL; + init_waitqueue_head(&priv->adapter->cmd_wait_q); + LEAVE(); +} + +/** + * @brief This function initializes firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + ENTER(); + if (fw == 0) { + sbi_enable_host_int(priv); + goto done; + } + sbi_disable_host_int(priv); + if (sbi_download_fw(priv)) { + PRINTM(ERROR, " FW failed to be download!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +#define FW_POLL_TRIES 100 +#define FW_RESET_REG 0x0EE +#define FW_RESET_VAL 0x99 + +/** + * @brief This function reload firmware + * + * @param priv A pointer to bt_private + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +static int +bt_reload_fw(bt_private *priv, int mode) +{ + int ret = 0, tries = 0; + u8 value = 1; + u32 reset_reg = FW_RESET_REG; + u8 reset_val = FW_RESET_VAL; + + ENTER(); + if ((mode != FW_RELOAD_SDIO_INBAND_RESET) && + (mode != FW_RELOAD_NO_EMULATION)) { + PRINTM(ERROR, "Invalid fw reload mode=%d\n", mode); + return -EFAULT; + } + + /** flush pending tx_queue */ + skb_queue_purge(&priv->adapter->tx_queue); + if (mode == FW_RELOAD_SDIO_INBAND_RESET) { + sbi_disable_host_int(priv); + /** Wake up firmware firstly */ + sbi_wakeup_firmware(priv); + + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = sd_write_reg(priv, reset_reg, 0xba); + if (!ret) { + ret = sd_read_reg(priv, reset_reg, &value); + if (!ret && (value == 0xba)) { + PRINTM(MSG, "Fw wake up\n"); + break; + } + } + udelay(1000); + } + + ret = sd_write_reg(priv, reset_reg, reset_val); + if (ret) { + PRINTM(ERROR, "Failed to write register.\n"); + goto done; + } + + /** Poll register around 1 ms */ + for (; tries < FW_POLL_TRIES; ++tries) { + ret = sd_read_reg(priv, reset_reg, &value); + if (ret) { + PRINTM(ERROR, "Failed to read register.\n"); + goto done; + } + if (value == 0) + /** FW is ready */ + break; + udelay(1000); + } + if (value) { + PRINTM(ERROR, + "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + } + + sbi_enable_host_int(priv); + /** reload FW */ + ret = bt_init_fw(priv); + if (ret) { + PRINTM(ERROR, "Re download firmware failed.\n"); + ret = -EFAULT; + } + LEAVE(); + return ret; +done: + sbi_enable_host_int(priv); + LEAVE(); + return ret; +} + +/** + * @brief This function request to reload firmware + * + * @param priv A pointer to bt_private + * @param mode fw reload mode. + * + * @return N/A + */ +void +bt_request_fw_reload(bt_private *priv, int mode) +{ + ENTER(); + if (mode == FW_RELOAD_WITH_EMULATION) { + bt_fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MSG, "BT: FW reload with re-emulation...\n"); + LEAVE(); + return; + } + /** Reload FW */ + priv->fw_reload = TRUE; + if (bt_reload_fw(priv, mode)) { + PRINTM(ERROR, "FW reload fail\n"); + goto done; + } + priv->fw_reload = FALSE; + /** Other operation here? */ +done: + LEAVE(); + return; +} + +/** + * @brief This function frees the structure of adapter + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_free_adapter(bt_private *priv) +{ + bt_adapter *adapter = priv->adapter; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->fwdump_queue); + kfree(adapter->tx_buffer); + kfree(adapter->hw_regs_buf); + /* Free allocated memory for fwdump filename */ + if (adapter->fwdump_fname) { + kfree(adapter->fwdump_fname); + adapter->fwdump_fname = NULL; + } + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; + + LEAVE(); +} + +/** + * @brief This function handles the wrapper_dev ioctl + * + * @param hev A pointer to wrapper_dev structure + * @cmd ioctl cmd + * @arg argument + * @return -ENOIOCTLCMD + */ +static int +mdev_ioctl(struct m_dev *m_dev, unsigned int cmd, void *arg) +{ + bt_private *priv = NULL; + int ret = 0; +#ifdef BLE_WAKEUP + u16 len; +#endif + + ENTER(); + + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Ioctl for unknown device (m_dev=NULL)\n"); + ret = -ENODEV; + goto done; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "HCI_RUNNING not set, flag=0x%lx\n", + m_dev->flags); + ret = -EBUSY; + goto done; + } + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { +#ifdef BLE_WAKEUP + case MBTCHAR_IOCTL_BLE_WAKEUP_PARAM: + PRINTM(MSG, "BT: Set ble wakeup parameters\n"); + if (copy_from_user(&len, arg, sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params length\n"); + ret = -EFAULT; + goto done; + } + /** Convert little endian length */ + len = __le16_to_cpu(len); + if (len < 2) { + PRINTM(ERROR, + "BT_IOCTL: Invalid ble wakeup params len %d\n", + len); + ret = -EFAULT; + goto done; + } + if ((len + sizeof(u16)) > priv->ble_wakeup_buf_size) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + priv->ble_wakeup_buf = + kzalloc(len + sizeof(u16), GFP_KERNEL); + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT_IOCTL: Fail to alloc buffer\t" + "for ble wakeup parameters\n"); + ret = -ENOMEM; + goto done; + } + priv->ble_wakeup_buf_size = len + sizeof(u16); + } + if (copy_from_user + (priv->ble_wakeup_buf, arg, len + sizeof(u16))) { + PRINTM(ERROR, + "BT_IOCTL: Fail to copy ble wakeup params\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_PARAM:", priv->ble_wakeup_buf, + len + sizeof(u16)); + break; +#endif + default: + break; + } + +done: +#ifdef BLE_WAKEUP + if (ret && priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + LEAVE(); + return ret; +} + +/** + * @brief This function handles wrapper device destruct + * + * @param m_dev A pointer to m_dev structure + * + * @return N/A + */ +static void +mdev_destruct(struct m_dev *m_dev) +{ + ENTER(); + LEAVE(); + return; +} + +/** + * @brief This function handles the wrapper device transmit + * + * @param m_dev A pointer to m_dev structure + * @param skb A pointer to sk_buff structure + * + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +mdev_send_frame(struct m_dev *m_dev, struct sk_buff *skb) +{ + bt_private *priv = NULL; + + ENTER(); + if (!m_dev || !m_dev->driver_data) { + PRINTM(ERROR, "Frame for unknown HCI device (m_dev=NULL)\n"); + LEAVE(); + return -ENODEV; + } + priv = (bt_private *)m_dev->driver_data; + if (!test_bit(HCI_RUNNING, &m_dev->flags)) { + PRINTM(ERROR, "Fail test HCI_RUNNING, flag=0x%lx\n", + m_dev->flags); + LEAVE(); + return -EBUSY; + } + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + m_dev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + m_dev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + m_dev->stat.sco_tx++; + break; + } + + if (m_dev->dev_type == DEBUG_TYPE) { + /* remember the ogf_ocf */ + priv->debug_device_pending = 1; + priv->debug_ocf_ogf[0] = skb->data[0]; + priv->debug_ocf_ogf[1] = skb->data[1]; + PRINTM(CMD, "debug_ocf_ogf[0]=0x%x debug_ocf_ogf[1]=0x%x\n", + priv->debug_ocf_ogf[0], priv->debug_ocf_ogf[1]); + } + + if (priv->adapter->tx_lock == TRUE) + skb_queue_tail(&priv->adapter->pending_queue, skb); + else + bt_queue_frame(priv, skb); + wake_up_interruptible(&priv->MainThread.waitQ); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function flushes the transmit queue + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_flush(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + skb_queue_purge(&priv->adapter->tx_queue); + skb_queue_purge(&priv->adapter->pending_queue); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function closes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS + */ +static int +mdev_close(struct m_dev *m_dev) +{ + + ENTER(); + mdev_req_lock(m_dev); + if (!test_and_clear_bit(HCI_UP, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + + if (m_dev->flush) + m_dev->flush(m_dev); + /* wait up pending read and unregister char dev */ + wake_up_interruptible(&m_dev->req_wait_q); + /* Drop queues */ + skb_queue_purge(&m_dev->rx_q); + if (!test_and_clear_bit(HCI_RUNNING, &m_dev->flags)) { + mdev_req_unlock(m_dev); + LEAVE(); + return 0; + } + module_put(THIS_MODULE); + m_dev->flags = 0; + mdev_req_unlock(m_dev); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function opens the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +static int +mdev_open(struct m_dev *m_dev) +{ + ENTER(); + + if (try_module_get(THIS_MODULE) == 0) + return BT_STATUS_FAILURE; + + set_bit(HCI_RUNNING, &m_dev->flags); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function queries the wrapper device + * + * @param m_dev A pointer to m_dev structure + * @param arg arguement + * + * @return BT_STATUS_SUCCESS or other + */ +void +mdev_query(struct m_dev *m_dev, void *arg) +{ + struct mbt_dev *mbt_dev = (struct mbt_dev *)m_dev->dev_pointer; + + ENTER(); + if (copy_to_user(arg, &mbt_dev->type, sizeof(mbt_dev->type))) + PRINTM(ERROR, "IOCTL_QUERY_TYPE: Fail copy to user\n"); + + LEAVE(); +} + +/** + * @brief This function initializes the wrapper device + * + * @param m_dev A pointer to m_dev structure + * + * @return BT_STATUS_SUCCESS or other + */ +void +init_m_dev(struct m_dev *m_dev) +{ + m_dev->dev_pointer = NULL; + m_dev->driver_data = NULL; + m_dev->dev_type = 0; + m_dev->spec_type = 0; + skb_queue_head_init(&m_dev->rx_q); + init_waitqueue_head(&m_dev->req_wait_q); + init_waitqueue_head(&m_dev->rx_wait_q); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + init_MUTEX(&m_dev->req_lock); +#else + sema_init(&m_dev->req_lock, 1); +#endif + spin_lock_init(&m_dev->rxlock); + memset(&m_dev->stat, 0, sizeof(struct hci_dev_stats)); + m_dev->open = mdev_open; + m_dev->close = mdev_close; + m_dev->flush = mdev_flush; + m_dev->send = mdev_send_frame; + m_dev->destruct = mdev_destruct; + m_dev->ioctl = mdev_ioctl; + m_dev->query = mdev_query; + m_dev->owner = THIS_MODULE; + +} + +/** + * @brief This function handles the major job in bluetooth driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to bt_thread structure + * @return BT_STATUS_SUCCESS + */ +static int +bt_service_main_thread(void *data) +{ + bt_thread *thread = data; + bt_private *priv = thread->priv; + bt_adapter *adapter = priv->adapter; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + wait_queue_t wait; +#else + wait_queue_entry_t wait; +#endif + struct sk_buff *skb; + ENTER(); + bt_activate_thread(thread); + init_waitqueue_entry(&wait, current); + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&thread->waitQ, &wait); + OS_SET_THREAD_STATE(TASK_INTERRUPTIBLE); + if (priv->adapter->WakeupTries || + ((!priv->adapter->IntCounter) && + (!priv->bt_dev.tx_dnld_rdy || + skb_queue_empty(&priv->adapter->tx_queue)) + && skb_queue_empty(&priv->adapter->fwdump_queue) + )) { + PRINTM(INFO, "Main: Thread sleeping...\n"); + schedule(); + } + OS_SET_THREAD_STATE(TASK_RUNNING); + remove_wait_queue(&thread->waitQ, &wait); + if (kthread_should_stop() || adapter->SurpriseRemoved) { + PRINTM(INFO, "main-thread: break from main thread: " + "SurpriseRemoved=0x%x\n", + adapter->SurpriseRemoved); + break; + } + + PRINTM(INFO, "Main: Thread waking up...\n"); + + if (priv->adapter->IntCounter) { + OS_INT_DISABLE; + adapter->IntCounter = 0; + OS_INT_RESTORE; + sbi_get_int_status(priv); + } else if ((priv->adapter->ps_state == PS_SLEEP) && + !skb_queue_empty(&priv->adapter->tx_queue)) { + priv->adapter->WakeupTries++; + sbi_wakeup_firmware(priv); + continue; + } + if (priv->adapter->ps_state == PS_SLEEP) + continue; + if (priv->bt_dev.tx_dnld_rdy == TRUE) { + if (!skb_queue_empty(&priv->adapter->tx_queue)) { + skb = skb_dequeue(&priv->adapter->tx_queue); + if (skb) + send_single_packet(priv, skb); + } + } + if (!skb_queue_empty(&priv->adapter->fwdump_queue)) { + skb = skb_dequeue(&priv->adapter->fwdump_queue); + if (skb) { + bt_store_firmware_dump(priv, skb->data, + skb->len); + dev_kfree_skb_any(skb); + } + } + } + bt_deactivate_thread(thread); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param m_dev A pointer to m_dev structure + * @return N/A + */ +void +bt_interrupt(struct m_dev *m_dev) +{ + bt_private *priv = (bt_private *)m_dev->driver_data; + ENTER(); + if (!priv || !priv->adapter) { + LEAVE(); + return; + } + PRINTM(INTR, "*\n"); + priv->adapter->ps_state = PS_AWAKE; + if (priv->adapter->hs_state == HS_ACTIVATED) { + PRINTM(CMD, "BT: %s: HS DEACTIVATED in ISR!\n", m_dev->name); + priv->adapter->hs_state = HS_DEACTIVATED; + } + priv->adapter->WakeupTries = 0; + priv->adapter->IntCounter++; + wake_up_interruptible(&priv->MainThread.waitQ); + LEAVE(); +} + +/** + * @brief Dynamic release of char dev + * + * @param kobj A pointer to kobject structure + * + * @return N/A + */ +static void +char_dev_release_dynamic(struct kobject *kobj) +{ + struct char_dev *cdev = container_of(kobj, struct char_dev, kobj); + ENTER(); + PRINTM(INFO, "free char_dev\n"); + kfree(cdev); + LEAVE(); +} + +static struct kobj_type ktype_char_dev_dynamic = { + .release = char_dev_release_dynamic, +}; + +/** + * @brief Allocation of char dev + * + * @param N/A + * + * @return char_dev + */ +static struct char_dev * +alloc_char_dev(void) +{ + struct char_dev *cdev; + ENTER(); + cdev = kzalloc(sizeof(struct char_dev), GFP_KERNEL); + if (cdev) { + kobject_init(&cdev->kobj, &ktype_char_dev_dynamic); + PRINTM(INFO, "alloc char_dev\n"); + } + return cdev; +} + +/** + * @brief Dynamic release of bt private + * + * @param kobj A pointer to kobject structure + * + * @return N/A + */ +static void +bt_private_dynamic_release(struct kobject *kobj) +{ + bt_private *priv = container_of(kobj, bt_private, kobj); + ENTER(); + PRINTM(INFO, "free bt priv\n"); + kfree(priv); + LEAVE(); +} + +static struct kobj_type ktype_bt_private_dynamic = { + .release = bt_private_dynamic_release, +}; + +/** + * @brief Allocation of bt private + * + * @param N/A + * + * @return bt_private + */ +static bt_private * +bt_alloc_priv(void) +{ + bt_private *priv; + ENTER(); + priv = kzalloc(sizeof(bt_private), GFP_KERNEL); + if (priv) { + kobject_init(&priv->kobj, &ktype_bt_private_dynamic); + PRINTM(INFO, "alloc bt priv\n"); + } + LEAVE(); + return priv; +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return kobject structure + */ +struct kobject * +bt_priv_get(bt_private *priv) +{ + PRINTM(INFO, "bt priv get object"); + return kobject_get(&priv->kobj); +} + +/** + * @brief Get bt private structure + * + * @param priv A pointer to bt_private structure + * + * @return N/A + */ +void +bt_priv_put(bt_private *priv) +{ + PRINTM(INFO, "bt priv put object"); + kobject_put(&priv->kobj); +} + +/** + * @brief This function send init commands to firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_init_cmd(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + ret = bt_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + if (ret < 0) { + PRINTM(FATAL, "Module cfg command send failed!\n"); + goto done; + } + if (btindrst != -1) { + ret = bt_set_independent_reset(priv); + if (ret < 0) { + PRINTM(FATAL, "Independent reset failed!\n"); + goto done; + } + } + if (btpmic) { + if (BT_STATUS_SUCCESS != bt_pmic_configure(priv)) { + PRINTM(FATAL, "BT: PMIC Configure failed \n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + ret = bt_set_ble_deepsleep(priv, deep_sleep ? TRUE : FALSE); + if (ret < 0) { + PRINTM(FATAL, "%s BLE deepsleep failed!\n", + deep_sleep ? "Enable" : "Disable"); + goto done; + } + if (psmode) { + priv->bt_dev.psmode = TRUE; + priv->bt_dev.idle_timeout = DEFAULT_IDLE_TIME; + ret = bt_enable_ps(priv); + if (ret < 0) { + PRINTM(FATAL, "Enable PS mode failed!\n"); + goto done; + } + } +#if defined(SDIO_SUSPEND_RESUME) + priv->bt_dev.gpio_gap = DEF_GPIO_GAP; + ret = bt_send_hscfg_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "Send HSCFG failed!\n"); + goto done; + } +#endif + priv->bt_dev.sdio_pull_cfg = 0xffffffff; + priv->bt_dev.sdio_pull_ctrl = 0; + wake_up_interruptible(&priv->MainThread.waitQ); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reinit firmware after redownload firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCESS/BT_STATUS_FAILURE + */ +int +bt_reinit_fw(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + priv->adapter->tx_lock = FALSE; + priv->adapter->ps_state = PS_AWAKE; + priv->adapter->suspend_fail = FALSE; + priv->adapter->is_suspended = FALSE; + priv->adapter->hs_skip = 0; + priv->adapter->num_cmd_timeout = 0; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + /* block all the packet from bluez */ + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) + priv->adapter->tx_lock = TRUE; + + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (init_cfg || cal_cfg || bt_mac || cal_cfg_ext) { + priv->adapter->tx_lock = FALSE; + bt_restore_tx_queue(priv); + } + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); +done: + return ret; +} + +/** + * @brief Module configuration and register device + * + * @param priv A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_conf_dpc(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct mbt_dev *mbt_dev = NULL; + struct debug_dev *debug_dev = NULL; + int i = 0; + struct char_dev *char_dev = NULL; + char dev_file[DEV_NAME_LEN + 5]; + unsigned char dev_type = 0; + + ENTER(); + + priv->bt_dev.tx_dnld_rdy = TRUE; + if (priv->fw_reload) { + bt_reinit_fw(priv); + LEAVE(); + return ret; + } + + if (drv_mode & DRV_MODE_BT) { + mbt_dev = alloc_mbt_dev(); + if (!mbt_dev) { + PRINTM(FATAL, "Can not allocate mbt dev\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + init_m_dev(&(priv->bt_dev.m_dev[BT_SEQ])); + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_TYPE; + priv->bt_dev.m_dev[BT_SEQ].spec_type = IANYWHERE_SPEC; + priv->bt_dev.m_dev[BT_SEQ].dev_pointer = (void *)mbt_dev; + priv->bt_dev.m_dev[BT_SEQ].driver_data = priv; + priv->bt_dev.m_dev[BT_SEQ].read_continue_flag = 0; + } + + dev_type = HCI_SDIO; + + if (mbt_dev) + mbt_dev->type = dev_type; + + ret = bt_init_cmd(priv); + if (ret < 0) { + PRINTM(FATAL, "BT init command failed!\n"); + goto done; + } + + if (mbt_dev && priv->bt_dev.devType == DEV_TYPE_AMP) { + mbt_dev->type |= HCI_BT_AMP; + priv->bt_dev.m_dev[BT_SEQ].dev_type = BT_AMP_TYPE; + } + /** Process device tree init parameters before register hci device. + * Since uplayer device has not yet registered, no need to block tx queue. + * */ + if (init_cfg) + if (BT_STATUS_SUCCESS != bt_init_config(priv, init_cfg)) { + PRINTM(FATAL, + "BT: Set user init data and param failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (cal_cfg) { + if (BT_STATUS_SUCCESS != bt_cal_config(priv, cal_cfg, bt_mac)) { + PRINTM(FATAL, "BT: Set cal data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } else if (bt_mac) { + PRINTM(INFO, + "Set BT mac_addr from insmod parametre bt_mac = %s\n", + bt_mac); + if (BT_STATUS_SUCCESS != bt_init_mac_address(priv, bt_mac)) { + PRINTM(FATAL, + "BT: Fail to set mac address from insmod parametre\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + if (cal_cfg_ext) { + if (BT_STATUS_SUCCESS != bt_cal_config_ext(priv, cal_cfg_ext)) { + PRINTM(FATAL, "BT: Set cal ext data failed\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + } + + /* Get FW version */ + bt_get_fw_version(priv); + snprintf((char *)priv->adapter->drv_ver, MAX_VER_STR_LEN, + mbt_driver_version, fw_version); + + if (mbt_dev) { + /** init mbt_dev */ + mbt_dev->flags = 0; + mbt_dev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); + mbt_dev->esco_type = (ESCO_HV1); + mbt_dev->link_mode = (HCI_LM_ACCEPT); + + mbt_dev->idle_timeout = 0; + mbt_dev->sniff_max_interval = 800; + mbt_dev->sniff_min_interval = 80; + for (i = 0; i < 3; i++) + mbt_dev->reassembly[i] = NULL; + atomic_set(&mbt_dev->promisc, 0); + + /** alloc char dev node */ + char_dev = alloc_char_dev(); + if (!char_dev) { + class_destroy(chardev_class); + ret = -ENOMEM; + goto err_kmalloc; + } + char_dev->minor = MBTCHAR_MINOR_BASE + mbtchar_minor; + if (mbt_dev->type & HCI_BT_AMP) + char_dev->dev_type = BT_AMP_TYPE; + else + char_dev->dev_type = BT_TYPE; + + if (bt_name) + snprintf(mbt_dev->name, sizeof(mbt_dev->name), "%s%d", + bt_name, mbtchar_minor); + else + snprintf(mbt_dev->name, sizeof(mbt_dev->name), + "mbtchar%d", mbtchar_minor); + snprintf(dev_file, sizeof(dev_file), "/dev/%s", mbt_dev->name); + mbtchar_minor++; + PRINTM(MSG, "BT: Create %s\n", dev_file); + + /** register m_dev to BT char device */ + priv->bt_dev.m_dev[BT_SEQ].index = char_dev->minor; + char_dev->m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + + /** create BT char device node */ + register_char_dev(char_dev, chardev_class, MODULE_NAME, + mbt_dev->name); + + /** chmod & chown for BT char device */ + mbtchar_chown(dev_file, AID_SYSTEM, AID_NET_BT_STACK); + mbtchar_chmod(dev_file, 0666); + + /** create proc device */ + snprintf(priv->bt_dev.m_dev[BT_SEQ].name, + sizeof(priv->bt_dev.m_dev[BT_SEQ].name), + mbt_dev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[BT_SEQ]), BT_SEQ); + } + + if ((debug_intf) && ((drv_mode & DRV_MODE_BT) + )) { + /** alloc debug_dev */ + debug_dev = alloc_debug_dev(); + if (!debug_dev) { + PRINTM(FATAL, "Can not allocate debug dev\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + + /** init m_dev */ + init_m_dev(&(priv->bt_dev.m_dev[DEBUG_SEQ])); + priv->bt_dev.m_dev[DEBUG_SEQ].dev_type = DEBUG_TYPE; + priv->bt_dev.m_dev[DEBUG_SEQ].spec_type = GENERIC_SPEC; + priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer = (void *)debug_dev; + priv->bt_dev.m_dev[DEBUG_SEQ].driver_data = priv; + + /** create char device for Debug */ + char_dev = alloc_char_dev(); + if (!char_dev) { + class_destroy(chardev_class); + ret = -ENOMEM; + goto err_kmalloc; + } + char_dev->minor = DEBUGCHAR_MINOR_BASE + debugchar_minor; + char_dev->dev_type = DEBUG_TYPE; + if (debug_name) + snprintf(debug_dev->name, sizeof(debug_dev->name), + "%s%d", debug_name, debugchar_minor); + else + snprintf(debug_dev->name, sizeof(debug_dev->name), + "mdebugchar%d", debugchar_minor); + snprintf(dev_file, sizeof(dev_file), "/dev/%s", + debug_dev->name); + PRINTM(MSG, "BT: Create %s\n", dev_file); + debugchar_minor++; + + /** register char dev */ + priv->bt_dev.m_dev[DEBUG_SEQ].index = char_dev->minor; + char_dev->m_dev = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + register_char_dev(char_dev, chardev_class, MODULE_NAME, + debug_dev->name); + + /** chmod for debug char device */ + mbtchar_chmod(dev_file, 0666); + + /** create proc device */ + snprintf(priv->bt_dev.m_dev[DEBUG_SEQ].name, + sizeof(priv->bt_dev.m_dev[DEBUG_SEQ].name), + debug_dev->name); + bt_proc_init(priv, &(priv->bt_dev.m_dev[DEBUG_SEQ]), DEBUG_SEQ); + } + +done: + LEAVE(); + return ret; +err_kmalloc: + LEAVE(); + return ret; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the bt_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to bt_private structure + */ + +bt_private * +bt_add_card(void *card) +{ + bt_private *priv = NULL; + int index = 0; + + ENTER(); + + priv = bt_alloc_priv(); + if (!priv) { + PRINTM(FATAL, "Can not allocate priv\n"); + LEAVE(); + return NULL; + } + /* Save the handle */ + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == NULL) + break; + } + if (index < MAX_BT_ADAPTER) { + m_priv[index] = priv; + } else { + PRINTM(ERROR, "Exceeded maximum cards supported!\n"); + goto err_kmalloc; + } + /* allocate buffer for bt_adapter */ + priv->adapter = kzalloc(sizeof(bt_adapter), GFP_KERNEL); + if (!priv->adapter) { + PRINTM(FATAL, "Allocate buffer for bt_adapter failed!\n"); + goto err_kmalloc; + } + priv->adapter->tx_buffer = + kzalloc(MAX_TX_BUF_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->tx_buffer) { + PRINTM(FATAL, "Allocate buffer for transmit\n"); + goto err_kmalloc; + } + priv->adapter->tx_buf = + (u8 *)ALIGN_ADDR(priv->adapter->tx_buffer, DMA_ALIGNMENT); + priv->adapter->hw_regs_buf = + kzalloc(SD_BLOCK_SIZE + DMA_ALIGNMENT, GFP_KERNEL); + if (!priv->adapter->hw_regs_buf) { + PRINTM(FATAL, "Allocate buffer for INT read buf failed!\n"); + goto err_kmalloc; + } + priv->adapter->hw_regs = + (u8 *)ALIGN_ADDR(priv->adapter->hw_regs_buf, DMA_ALIGNMENT); + bt_init_adapter(priv); + + PRINTM(INFO, "Starting kthread...\n"); + priv->MainThread.priv = priv; + spin_lock_init(&priv->driver_lock); + + bt_create_thread(bt_service_main_thread, &priv->MainThread, + "bt_main_service"); + + /* wait for mainthread to up */ + while (!priv->MainThread.pid) + os_sched_timeout(1); + + /** user config file */ + init_waitqueue_head(&priv->init_user_conf_wait_q); + + priv->bt_dev.card = card; + + ((struct sdio_mmc_card *)card)->priv = priv; + priv->adapter->sd_ireg = 0; + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (sbi_register_dev(priv) < 0) { + PRINTM(FATAL, "Failed to register bt device!\n"); + goto err_registerdev; + } + if (bt_init_fw(priv)) { + PRINTM(FATAL, "BT Firmware Init Failed\n"); + goto err_init_fw; + } + LEAVE(); + return priv; + +err_init_fw: + clean_up_m_devs(priv); + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); +err_registerdev: + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); +err_kmalloc: + if (priv->adapter) + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return NULL; +} + +/** + * @brief This function send hardware remove event + * + * @param priv A pointer to bt_private + * @return N/A + */ +void +bt_send_hw_remove_event(bt_private *priv) +{ + struct sk_buff *skb = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + ENTER(); + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; +#define HCI_HARDWARE_ERROR_EVT 0x10 +#define HCI_HARDWARE_REMOVE 0x24 + skb = bt_skb_alloc(3, GFP_ATOMIC); + skb->data[0] = HCI_HARDWARE_ERROR_EVT; + skb->data[1] = 1; + skb->data[2] = HCI_HARDWARE_REMOVE; + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 3); + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + PRINTM(MSG, "Send HW ERROR event\n"); + if (!mdev_recv_frame(skb)) { +#define RX_WAIT_TIMEOUT 300 + mdev_bt->wait_rx_complete = TRUE; + mdev_bt->rx_complete_flag = FALSE; + if (os_wait_interruptible_timeout + (mdev_bt->rx_wait_q, mdev_bt->rx_complete_flag, + RX_WAIT_TIMEOUT)) + PRINTM(MSG, "BT stack received the event\n"); + mdev_bt->stat.byte_rx += 3; + } + } + LEAVE(); + return; +} + +#ifdef BLE_WAKEUP +/** + * @brief This function used to config BLE wakeup pattern + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +int +bt_config_ble_wakeup(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + u16 ocf, left_len; + u8 len, more_cmd; + u8 *pos; + + ENTER(); + + if (!priv->ble_wakeup_buf) { + PRINTM(ERROR, "BT: no ble wakeup parameters found\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + + PRINTM(MSG, "Config ble wakeup pattern\n"); + + pos = priv->ble_wakeup_buf; + left_len = *(u16 *) pos; + left_len = __le16_to_cpu(left_len); + pos += sizeof(u16); + + while (left_len >= 2) { + more_cmd = *pos; + len = *(pos + 1); + if (((len + 2) > left_len) || + (!more_cmd && ((len + 2) < left_len))) { + PRINTM(ERROR, "Invalid ble parameters\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + skb = bt_skb_alloc(len, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "BT BLE WAKEUP: fail to alloc skb\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy(skb_put(skb, len), pos + 2, len); + bt_cb(skb)->pkt_type = *(u8 *)skb->data; + skb_pull(skb, 1); + DBG_HEXDUMP(DAT_D, "BLE_WAKEUP_CMD:", skb->data, skb->len); + skb->dev = (void *)(&(priv->bt_dev.m_dev[BT_SEQ])); + skb_queue_head(&priv->adapter->tx_queue, skb); + priv->bt_dev.sendcmdflag = TRUE; + priv->bt_dev.send_cmd_opcode = *(u16 *) skb->data; + ocf = hci_opcode_ocf(priv->bt_dev.send_cmd_opcode); + priv->adapter->cmd_complete = FALSE; + + wake_up_interruptible(&priv->MainThread.waitQ); + if (!os_wait_interruptible_timeout + (priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, + WAIT_UNTIL_CMD_RESP)) { + ret = BT_STATUS_FAILURE; + PRINTM(ERROR, + "BT: Set Set ble wakeup cmd 0x%x timeout:\n", + priv->bt_dev.send_cmd_opcode); + bt_cmd_timeout_func(priv, ocf); + goto done; + } + + pos += len + 2; + left_len -= len + 2; + } + +done: + if (ret != BT_STATUS_SUCCESS) { + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function send system suspend event + * + * @param priv A pointer to bt_private + * @return BT_STATUS_SUCCESS + */ +int +bt_send_system_event(bt_private *priv, u8 flag) +{ + struct sk_buff *skb = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + + ENTER(); + + if (!priv->bt_dev.m_dev[BT_SEQ].dev_pointer) { + LEAVE(); + return BT_STATUS_FAILURE; + } + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + + skb = bt_skb_alloc(4, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "Fail to allocate sys suspend event skb\n"); + return BT_STATUS_FAILURE; + } + skb->data[0] = VENDOR_SPECIFIC_EVENT; + skb->data[1] = 2; + skb->data[2] = HCI_SYSTEM_SUSPEND_EVT; + if (flag) + skb->data[3] = HCI_SYSTEM_SUSPEND; + else + skb->data[3] = HCI_SYSTEM_RESUME; + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + skb_put(skb, 4); + if (mbt_dev) { + skb->dev = (void *)mdev_bt; + PRINTM(MSG, "Send system %s event\n", + flag ? "suspend" : "resume"); + if (!mdev_recv_frame(skb)) { +#define RX_WAIT_TIMEOUT 300 + mdev_bt->wait_rx_complete = TRUE; + mdev_bt->rx_complete_flag = FALSE; + if (os_wait_interruptible_timeout(mdev_bt->rx_wait_q, + mdev_bt-> + rx_complete_flag, + RX_WAIT_TIMEOUT)) + PRINTM(MSG, "BT stack received the event\n"); + mdev_bt->stat.byte_rx += 4; + } + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function removes the card. + * + * @param card A pointer to card + * @return BT_STATUS_SUCCESS + */ +int +bt_remove_card(void *card) +{ + bt_private *priv = (bt_private *)card; + int index; + ENTER(); + if (!priv) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv->adapter->SurpriseRemoved = TRUE; + + bt_send_hw_remove_event(priv); +#ifdef BLE_WAKEUP + if (priv->ble_wakeup_buf) { + kfree(priv->ble_wakeup_buf); + priv->ble_wakeup_buf = NULL; + priv->ble_wakeup_buf_size = 0; + } +#endif + wake_up_interruptible(&priv->adapter->cmd_wait_q); + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) { + os_sched_timeout(1); + wake_up_interruptible(&priv->MainThread.waitQ); + } + + bt_proc_remove(priv); + PRINTM(INFO, "Unregister device\n"); + sbi_unregister_dev(priv); + clean_up_m_devs(priv); + PRINTM(INFO, "Free Adapter\n"); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function initializes module. + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +bt_init_module(void) +{ + int ret = BT_STATUS_SUCCESS; + int index; + ENTER(); + PRINTM(MSG, "BT: Loading driver\n"); + /* Init the bt_private pointer array first */ + for (index = 0; index < MAX_BT_ADAPTER; index++) + m_priv[index] = NULL; + bt_root_proc_init(); + + /** create char device class */ + chardev_class = class_create(THIS_MODULE, MODULE_NAME); + if (IS_ERR(chardev_class)) { + PRINTM(ERROR, "Unable to allocate class\n"); + bt_root_proc_remove(); + ret = PTR_ERR(chardev_class); + goto done; + } + + if (sbi_register() == NULL) { + bt_root_proc_remove(); + ret = BT_STATUS_FAILURE; + goto done; + } +done: + if (ret) + PRINTM(MSG, "BT: Driver loading failed\n"); + else + PRINTM(MSG, "BT: Driver loaded successfully\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void +bt_exit_module(void) +{ + bt_private *priv; + int index; + ENTER(); + PRINTM(MSG, "BT: Unloading driver\n"); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + priv = m_priv[index]; + if (!priv) + continue; + if (priv && !priv->adapter->SurpriseRemoved) { + if (BT_STATUS_SUCCESS == bt_send_reset_command(priv)) + bt_send_module_cfg_cmd(priv, + MODULE_SHUTDOWN_REQ); + } + sbi_disable_host_int(priv); + + } + + sbi_unregister(); + + bt_root_proc_remove(); + class_destroy(chardev_class); + PRINTM(MSG, "BT: Driver unloaded\n"); + LEAVE(); +} + +module_init(bt_init_module); +module_exit(bt_exit_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell Bluetooth Driver Ver. " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); +module_param(fw, int, 0); +MODULE_PARM_DESC(fw, "0: Skip firmware download; otherwise: Download firmware"); +module_param(psmode, int, 0); +MODULE_PARM_DESC(psmode, "1: Enable powermode; 0: Disable powermode"); +module_param(deep_sleep, int, 0); +MODULE_PARM_DESC(deep_sleep, "1: Enable deep sleep; 0: Disable deep sleep"); +#ifdef CONFIG_OF +module_param(dts_enable, int, 0); +MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS"); +#endif +#ifdef DEBUG_LEVEL1 +module_param(mbt_drvdbg, uint, 0); +MODULE_PARM_DESC(mbt_drvdbg, "BIT3:DBG_DATA BIT4:DBG_CMD 0xFF:DBG_ALL"); +#endif +#ifdef SDIO_SUSPEND_RESUME +module_param(mbt_pm_keep_power, int, 0); +MODULE_PARM_DESC(mbt_pm_keep_power, "1: PM keep power; 0: PM no power"); +#endif +module_param(init_cfg, charp, 0); +MODULE_PARM_DESC(init_cfg, "BT init config file name"); +module_param(cal_cfg, charp, 0); +MODULE_PARM_DESC(cal_cfg, "BT calibrate file name"); +module_param(cal_cfg_ext, charp, 0); +MODULE_PARM_DESC(cal_cfg_ext, "BT calibrate ext file name"); +module_param(bt_mac, charp, 0660); +MODULE_PARM_DESC(bt_mac, "BT init mac address"); +module_param(drv_mode, int, 0); +MODULE_PARM_DESC(drv_mode, "Bit 0: BT/AMP/BLE;"); +module_param(bt_name, charp, 0); +MODULE_PARM_DESC(bt_name, "BT interface name"); +module_param(debug_intf, int, 0); +MODULE_PARM_DESC(debug_intf, + "1: Enable debug interface; 0: Disable debug interface "); +module_param(debug_name, charp, 0); +MODULE_PARM_DESC(debug_name, "Debug interface name"); +module_param(bt_fw_reload, int, 0); +MODULE_PARM_DESC(bt_fw_reload, + "0: disable fw_reload; 1: enable fw reload feature"); +module_param(btindrst, int, 0); +MODULE_PARM_DESC(btindrst, + "Independent reset configuration; high byte:GPIO pin number;low byte:0x0:disable, 0x1:out-band reset, 0x2:in-band reset."); +module_param(btpmic, int, 0); +MODULE_PARM_DESC(btpmic, + "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware (default)"); +module_param(bt_fw_serial, int, 0); +MODULE_PARM_DESC(bt_fw_serial, + "0: Support parallel download FW; 1: Support serial download FW");
diff --git a/bt_sd8997/bt_char/bt_proc.c b/bt_sd8997/bt_char/bt_proc.c new file mode 100644 index 0000000..d2fa99a --- /dev/null +++ b/bt_sd8997/bt_char/bt_proc.c
@@ -0,0 +1,893 @@ +/** @file bt_proc.c + * + * @brief This file handle the functions for proc files + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/proc_fs.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** proc diretory root */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#define PROC_DIR NULL +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#define PROC_DIR (&proc_root) +#else +#define PROC_DIR proc_net +#endif + +/** Proc mbt directory entry */ +static struct proc_dir_entry *proc_mbt; + +#define CMD52_STR_LEN 50 +static char cmd52_string[CMD52_STR_LEN]; + +/** proc data structure */ +struct proc_data { + /** Read length */ + int rdlen; + /** Read buffer */ + char *rdbuf; + /** Write length */ + int wrlen; + /** Maximum write length */ + int maxwrlen; + /** Write buffer */ + char *wrbuf; + /** Private structure */ + struct _bt_private *pbt; + void (*on_close) (struct inode *, struct file *); +}; + +/** Default file permission */ +#define DEFAULT_FILE_PERM 0644 + +/** Bluetooth device offset */ +#define OFFSET_BT_DEV 0x01 +/** Bluetooth adapter offset */ +#define OFFSET_BT_ADAPTER 0x02 +/** Show integer */ +#define SHOW_INT 0x10 +/** Show hex */ +#define SHOW_HEX 0x20 +/** Show string */ +#define SHOW_STRING 0x40 + +/** Device size */ +#define item_dev_size(n) (sizeof((bt_dev_t *)0)->n) +/** Device address */ +#define item_dev_addr(n) ((t_ptr) &((bt_dev_t *)0)->n) + +/** Adapter size */ +#define item_adapter_size(n) (sizeof((bt_adapter *)0)->n) +/** Adapter address */ +#define item_adapter_addr(n) ((t_ptr) &((bt_adapter *)0)->n) + +static struct item_data config_items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(u32), (t_ptr)&mbt_drvdbg, 0, SHOW_HEX} + , +#endif + {"idle_timeout", item_dev_size(idle_timeout), 0, + item_dev_addr(idle_timeout), OFFSET_BT_DEV | SHOW_HEX} + , + {"psmode", item_dev_size(psmode), 0, item_dev_addr(psmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"pscmd", item_dev_size(pscmd), 0, item_dev_addr(pscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"hsmode", item_dev_size(hsmode), 0, item_dev_addr(hsmode), + OFFSET_BT_DEV | SHOW_INT} + , + {"hscmd", item_dev_size(hscmd), 0, item_dev_addr(hscmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"gpio_gap", item_dev_size(gpio_gap), 0, item_dev_addr(gpio_gap), + OFFSET_BT_DEV | SHOW_HEX} + , + {"hscfgcmd", item_dev_size(hscfgcmd), 0, item_dev_addr(hscfgcmd), + OFFSET_BT_DEV | SHOW_INT} + , + {"sdio_pull_cfg", item_dev_size(sdio_pull_cfg), 0, + item_dev_addr(sdio_pull_cfg), OFFSET_BT_DEV | SHOW_HEX} + , + {"sdio_pull_ctrl", item_dev_size(sdio_pull_ctrl), 0, + item_dev_addr(sdio_pull_ctrl), OFFSET_BT_DEV | SHOW_INT} + , + {"test_mode", item_dev_size(test_mode), 0, item_dev_addr(test_mode), + OFFSET_BT_DEV | SHOW_INT} + , + +}; + +static struct item_data status_items[] = { + {"version", item_adapter_size(drv_ver), 0, item_adapter_addr(drv_ver), + OFFSET_BT_ADAPTER | SHOW_STRING}, + {"tx_dnld_rdy", item_dev_size(tx_dnld_rdy), 0, + item_dev_addr(tx_dnld_rdy), + OFFSET_BT_DEV | SHOW_INT}, + {"psmode", item_adapter_size(psmode), 0, item_adapter_addr(psmode), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_state", item_adapter_size(hs_state), 0, + item_adapter_addr(hs_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"hs_skip", item_adapter_size(hs_skip), 0, item_adapter_addr(hs_skip), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"ps_state", item_adapter_size(ps_state), 0, + item_adapter_addr(ps_state), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"WakeupTries", item_adapter_size(WakeupTries), 0, + item_adapter_addr(WakeupTries), OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_recv", item_adapter_size(irq_recv), 0, + item_adapter_addr(irq_recv), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"irq_done", item_adapter_size(irq_done), 0, + item_adapter_addr(irq_done), + OFFSET_BT_ADAPTER | SHOW_INT}, + {"skb_pending", item_adapter_size(skb_pending), 0, + item_adapter_addr(skb_pending), OFFSET_BT_ADAPTER | SHOW_INT}, +}; + +static struct item_data debug_items[] = { + {"sdcmd52rw", 0, (t_ptr)cmd52_string, 0, SHOW_STRING}, +}; + +/** + * @brief convert string to number + * + * @param s pointer to numbered string + * @return converted number from string s + */ +int +string_to_number(char *s) +{ + int r = 0; + int base = 0; + int pn = 1; + + if (strncmp(s, "-", 1) == 0) { + pn = -1; + s++; + } + if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0)) { + base = 16; + s += 2; + } else + base = 10; + + for (s = s; *s != 0; s++) { + if ((*s >= '0') && (*s <= '9')) + r = (r * base) + (*s - '0'); + else if ((*s >= 'A') && (*s <= 'F')) + r = (r * base) + (*s - 'A' + 10); + else if ((*s >= 'a') && (*s <= 'f')) + r = (r * base) + (*s - 'a' + 10); + else + break; + } + + return r * pn; +} + +/** + * @brief Create cmd52 string + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +static int +form_cmd52_string(bt_private *priv) +{ + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + snprintf(cmd52_string, CMD52_STR_LEN - 1, "BT: %d 0x%0x 0x%02X", + priv->bt_dev.cmd52_func, priv->bt_dev.cmd52_reg, + priv->bt_dev.cmd52_val); + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/* + * @brief Parse cmd52 string + * + * @param buffer A pointer user buffer + * @param len Length of user buffer + * @param func Parsed func number + * @param reg Parsed reg value + * @param val Parsed value to set + * @return BT_STATUS_SUCCESS + */ +static int +parse_cmd52_string(const char __user * buffer, size_t len, + int *func, int *reg, int *val) +{ + int ret = BT_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + + ENTER(); + + string = kzalloc(CMD52_STR_LEN, GFP_KERNEL); + if (!string) { + PRINTM(ERROR, "BT: Can not alloc mem for cmd52 string\n"); + LEAVE(); + return -ENOMEM; + } + memcpy(string, buffer + strlen("sdcmd52rw="), + len - strlen("sdcmd52rw=")); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) + *func = string_to_number(pos); + + /* Get reg */ + pos = strsep(&string, " \t"); + if (pos) + *reg = string_to_number(pos); + + /* Get val (optional) */ + pos = strsep(&string, " \t"); + if (pos) + *val = string_to_number(pos); + kfree(string); + LEAVE(); + return ret; +} + +/** + * @brief This function handle generic proc file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS + */ +static int +proc_close(struct inode *inode, struct file *file) +{ + struct proc_data *pdata = file->private_data; + ENTER(); + if (pdata) { + if (pdata->on_close != NULL) + pdata->on_close(inode, file); + kfree(pdata->rdbuf); + kfree(pdata->wrbuf); + kfree(pdata); + } + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function handle generic proc file read + * + * @param file A pointer to file structure + * @param buffer A pointer to output buffer + * @param len number of byte to read + * @param offset A pointer to offset of file + * @return number of output data + */ +static ssize_t +proc_read(struct file *file, char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + if ((!pdata->rdbuf) || (pos < 0)) + return -EINVAL; + if (pos >= pdata->rdlen) + return 0; + if (len > pdata->rdlen - pos) + len = pdata->rdlen - pos; + if (copy_to_user(buffer, pdata->rdbuf + pos, len)) + return -EFAULT; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle generic proc file write + * + * @param file A pointer to file structure + * @param buffer A pointer to input buffer + * @param len number of byte to write + * @param offset A pointer to offset of file + * @return number of input data + */ +static ssize_t +proc_write(struct file *file, + const char __user * buffer, size_t len, loff_t * offset) +{ + loff_t pos = *offset; + struct proc_data *pdata = (struct proc_data *)file->private_data; + int func = 0, reg = 0, val = 0; + int config_data = 0; + char *line = NULL; + + if (!pdata->wrbuf || (pos < 0)) + return -EINVAL; + if (pos >= pdata->maxwrlen) + return 0; + if (len > pdata->maxwrlen - pos) + len = pdata->maxwrlen - pos; + if (copy_from_user(pdata->wrbuf + pos, buffer, len)) + return -EFAULT; + if (!strncmp(pdata->wrbuf + pos, "fw_reload", strlen("fw_reload"))) { + if (!strncmp + (pdata->wrbuf + pos, "fw_reload=", strlen("fw_reload="))) { + line = pdata->wrbuf + pos; + line += strlen("fw_reload") + 1; + config_data = string_to_number(line); + } else + config_data = FW_RELOAD_SDIO_INBAND_RESET; + PRINTM(MSG, "Request fw_reload=%d\n", config_data); + bt_request_fw_reload(pdata->pbt, config_data); + } + if (!strncmp(pdata->wrbuf + pos, "sdcmd52rw=", strlen("sdcmd52rw="))) { + parse_cmd52_string(pdata->wrbuf + pos, len, &func, ®, &val); + sd_write_cmd52_val(pdata->pbt, func, reg, val); + } + if (!strncmp(pdata->wrbuf + pos, "debug_dump", strlen("debug_dump"))) { + bt_dump_sdio_regs(pdata->pbt); + bt_dump_firmware_info_v2(pdata->pbt); + } + + if (pos + len > pdata->wrlen) + pdata->wrlen = len + file->f_pos; + *offset = pos + len; + return len; +} + +/** + * @brief This function handle the generic file close + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return N/A + */ +static void +proc_on_close(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata = file->private_data; + char *line; + int i; + ENTER(); + if (!pdata->wrlen) + return; + line = pdata->wrbuf; + while (line[0]) { + for (i = 0; i < priv->num_items; i++) { + if (!strncmp + (line, priv->pdata[i].name, + strlen(priv->pdata[i].name))) { + line += strlen(priv->pdata[i].name) + 1; + if (priv->pdata[i].size == 1) + *((u8 *)priv->pdata[i].addr) = + (u8)string_to_number(line); + else if (priv->pdata[i].size == 2) + *((u16 *) priv->pdata[i].addr) = + (u16) string_to_number(line); + else if (priv->pdata[i].size == 4) + *((u32 *)priv->pdata[i].addr) = + (u32)string_to_number(line); + } + } + while (line[0] && line[0] != '\n') + line++; + if (line[0]) + line++; + } + if (priv->pbt->bt_dev.hscmd || priv->pbt->bt_dev.pscmd + || priv->pbt->bt_dev.sdio_pull_ctrl + || priv->pbt->bt_dev.test_mode || priv->pbt->bt_dev.hscfgcmd) { + bt_prepare_command(priv->pbt); + wake_up_interruptible(&priv->pbt->MainThread.waitQ); + } + LEAVE(); + return; +} + +/** + * @brief This function handle the generic file open + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + struct proc_private_data *priv = PDE_DATA(inode); +#else + struct proc_private_data *priv = PDE(inode)->data; +#endif + struct proc_data *pdata; + int i; + char *p; + u32 val = 0; + ENTER(); + priv->pbt->adapter->skb_pending = + skb_queue_len(&priv->pbt->adapter->tx_queue); + file->private_data = kzalloc(sizeof(struct proc_data), GFP_KERNEL); + if (file->private_data == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for proc_data\n"); + LEAVE(); + return -ENOMEM; + } + pdata = (struct proc_data *)file->private_data; + pdata->pbt = priv->pbt; + pdata->rdbuf = kmalloc(priv->bufsize, GFP_KERNEL); + if (pdata->rdbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for rdbuf\n"); + kfree(file->private_data); + LEAVE(); + return -ENOMEM; + } + if (priv->fileflag == DEFAULT_FILE_PERM) { + pdata->wrbuf = kzalloc(priv->bufsize, GFP_KERNEL); + if (pdata->wrbuf == NULL) { + PRINTM(ERROR, "BT: Can not alloc mem for wrbuf\n"); + kfree(pdata->rdbuf); + kfree(file->private_data); + return -ENOMEM; + } + pdata->maxwrlen = priv->bufsize; + pdata->on_close = proc_on_close; + } + p = pdata->rdbuf; + for (i = 0; i < priv->num_items; i++) { + if (priv->pdata[i].size == 1) + val = *((u8 *)priv->pdata[i].addr); + else if (priv->pdata[i].size == 2) + val = *((u16 *) priv->pdata[i].addr); + else if (priv->pdata[i].size == 4) + val = *((u32 *)priv->pdata[i].addr); + if (priv->pdata[i].flag & SHOW_INT) + p += sprintf(p, "%s=%d\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_HEX) + p += sprintf(p, "%s=0x%x\n", priv->pdata[i].name, val); + else if (priv->pdata[i].flag & SHOW_STRING) { + if (!strncmp + (priv->pdata[i].name, "sdcmd52rw", + strlen("sdcmd52rw"))) { + sd_read_cmd52_val(priv->pbt); + form_cmd52_string(priv->pbt); + } + p += sprintf(p, "%s=%s\n", priv->pdata[i].name, + (char *)priv->pdata[i].addr); + } + } + pdata->rdlen = strlen(pdata->rdbuf); + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** Proc read ops */ +static const struct file_operations proc_read_ops = { + .read = proc_read, + .open = proc_open, + .release = proc_close +}; + +/** Proc Read-Write ops */ +static const struct file_operations proc_rw_ops = { + .read = proc_read, + .write = proc_write, + .open = proc_open, + .release = proc_close +}; + +static struct proc_private_data proc_files[] = { + {"status", S_IRUGO, 1024, + sizeof(status_items) / sizeof(status_items[0]), + &status_items[0], NULL, &proc_read_ops} + , + {"config", DEFAULT_FILE_PERM, 512, + sizeof(config_items) / sizeof(config_items[0]), &config_items[0], NULL, + &proc_rw_ops} + , + {"debug", DEFAULT_FILE_PERM, 512, + sizeof(debug_items) / sizeof(debug_items[0]), &debug_items[0], NULL, + &proc_rw_ops} + , +}; + +/** + * @brief Proc read function for histogram + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +bt_histogram_read(struct seq_file *sfp, void *data) +{ + bt_hist_proc_data *pdata = (bt_hist_proc_data *) sfp->private; + bt_private *priv = (bt_private *)pdata->pbt; + u8 ant_num; + int i, j; + + ENTER(); + if (!priv) { + LEAVE(); + return -EFAULT; + } + bt_get_histogram(priv); + ant_num = priv->hist_data_len / sizeof(bt_histogram_data); + seq_printf(sfp, "BT histogram:\n"); + seq_printf(sfp, "antenna: 0=2.4G antenna a, 1=2.4G antenna b\n\n"); + if (ant_num < 1) { + seq_printf(sfp, "no histogram data from FW\n"); + LEAVE(); + return 0; + } + for (i = 0; i < ant_num; i++) { + if (pdata->antenna != priv->hist_data[i].antenna) + continue; + seq_printf(sfp, "antenna %d\n", priv->hist_data[i].antenna); + switch (priv->hist_data[i].powerclass) { + case 2: + seq_printf(sfp, "Power class=1.5\n"); + break; + case 5: + seq_printf(sfp, "Power class=2\n"); + break; + case 6: + seq_printf(sfp, "Power class=1\n"); + break; + default: + seq_printf(sfp, "Power class=%d\n", + priv->hist_data[i].powerclass); + break; + } + for (j = 0; j < (MAX_BT_LINK + MAX_BLE_LINK); j++) { + switch (priv->hist_data[i].link[j].txrxrate) { + case BDR_RATE_1M: + seq_printf(sfp, + "BT link[%d]: TxPower=%d dBm, TxRx Rate=BDR(1 mbps), RSSI=%d dBm\n", + j + 1, + priv->hist_data[i].link[j].txpower, + priv->hist_data[i].link[j].rssi); + break; + case EDR_RATE_2_3M: + seq_printf(sfp, + "BT link[%d]: TxPower=%d dBm, TxRx Rate=EDR(2/3 mbps), RSSI=%d dBm\n", + j + 1, + priv->hist_data[i].link[j].txpower, + priv->hist_data[i].link[j].rssi); + break; + case BLE_RATE_1M: + seq_printf(sfp, + "BLE link[%d]: TxPower=%d dBm, TxRx Rate=BLE(1 mbps), RSSI=%d dBm\n", + j - MAX_BT_LINK + 0x80, + priv->hist_data[i].link[j].txpower, + priv->hist_data[i].link[j].rssi); + break; + default: + if (j < MAX_BT_LINK) + seq_printf(sfp, + "BT link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n", + j + 1, + priv->hist_data[i].link[j]. + txpower, + priv->hist_data[i].link[j]. + txrxrate, + priv->hist_data[i].link[j]. + rssi); + else + seq_printf(sfp, + "BLE link[%d]: TxPower=%d dBm, TxRx Rate=(%d), RSSI=%d dBm\n", + j - MAX_BT_LINK + 0x80, + priv->hist_data[i].link[j]. + txpower, + priv->hist_data[i].link[j]. + txrxrate, + priv->hist_data[i].link[j]. + rssi); + break; + } + } + seq_printf(sfp, "\n"); + } + LEAVE(); + return 0; +} + +/** + * @brief Proc open function for histogram + * + * @param inode A pointer to inode structure + * @param file A pointer to file structure + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int +bt_histogram_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, bt_histogram_read, PDE_DATA(inode)); +#else + return single_open(file, bt_histogram_read, PDE(inode)->data); +#endif +} + +/** Histogram proc fops */ +static const struct file_operations histogram_proc_fops = { + .owner = THIS_MODULE, + .open = bt_histogram_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * @brief This function initializes proc entry + * + * @param priv A pointer to bt_private structure + * @param m_dev A pointer to struct m_dev + * @param seq Sequence number + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_proc_init(bt_private *priv, struct m_dev *m_dev, int seq) +{ + int ret = BT_STATUS_SUCCESS; + struct proc_dir_entry *entry; + int i, j; + char hist_entry[50]; + ENTER(); + + memset(cmd52_string, 0, CMD52_STR_LEN); + if (proc_mbt) { + priv->dev_proc[seq].proc_entry = + proc_mkdir(m_dev->name, proc_mbt); + if (!priv->dev_proc[seq].proc_entry) { + PRINTM(ERROR, "BT: Could not mkdir %s!\n", m_dev->name); + ret = BT_STATUS_FAILURE; + goto done; + } + priv->dev_proc[seq].hist_entry = + proc_mkdir("histogram", priv->dev_proc[seq].proc_entry); + if (!priv->dev_proc[seq].hist_entry) { + PRINTM(ERROR, "BT: Could not mkdir histogram!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + for (i = 0; i < MAX_ANTENNA_NUM; i++) { + priv->hist_proc[i].antenna = i; + priv->hist_proc[i].pbt = priv; + snprintf(hist_entry, sizeof(hist_entry), "bt-ant%d", i); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + entry = proc_create_data(hist_entry, 0644, + priv->dev_proc[seq].hist_entry, + &histogram_proc_fops, + &priv->hist_proc[i]); + if (entry == NULL) +#else + entry = create_proc_entry(hist_entry, 0644, + priv->dev_proc[seq]. + hist_entry); + if (entry) { + entry->data = &priv->hist_proc[i]; + entry->proc_fops = &histogram_proc_fops; + } else +#endif + { + PRINTM(MSG, + "Fail to create histogram proc %s\n", + hist_entry); + ret = BT_STATUS_FAILURE; + goto done; + } + } + priv->dev_proc[seq].pfiles = + kmalloc(sizeof(proc_files), GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles) { + PRINTM(ERROR, + "BT: Could not alloc memory for pfile!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles, (u8 *)proc_files, + sizeof(proc_files)); + priv->dev_proc[seq].num_proc_files = ARRAY_SIZE(proc_files); + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) + priv->dev_proc[seq].pfiles[j].pdata = NULL; + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + priv->dev_proc[seq].pfiles[j].pdata = + kmalloc(priv->dev_proc[seq].pfiles[j]. + num_items * sizeof(struct item_data), + GFP_ATOMIC); + if (!priv->dev_proc[seq].pfiles[j].pdata) { + PRINTM(ERROR, + "BT: Could not alloc memory for pdata!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + memcpy((u8 *)priv->dev_proc[seq].pfiles[j].pdata, + (u8 *)proc_files[j].pdata, + priv->dev_proc[seq].pfiles[j].num_items * + sizeof(struct item_data)); + for (i = 0; i < priv->dev_proc[seq].pfiles[j].num_items; + i++) { + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_DEV) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)&priv->bt_dev; + if (priv->dev_proc[seq].pfiles[j]. + pdata[i].flag & OFFSET_BT_ADAPTER) + priv->dev_proc[seq].pfiles[j].pdata[i]. + addr = + priv->dev_proc[seq].pfiles[j]. + pdata[i].offset + + (t_ptr)priv->adapter; + } + priv->dev_proc[seq].pfiles[j].pbt = priv; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + entry = proc_create_data(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq].proc_entry, + proc_files[j].fops, + &priv->dev_proc[seq]. + pfiles[j]); + if (entry == NULL) +#else + entry = create_proc_entry(proc_files[j].name, + S_IFREG | proc_files[j]. + fileflag, + priv->dev_proc[seq]. + proc_entry); + if (entry) { + entry->data = &priv->dev_proc[seq].pfiles[j]; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + entry->owner = THIS_MODULE; +#endif + entry->proc_fops = proc_files[j].fops; + } else +#endif + PRINTM(MSG, "BT: Fail to create proc %s\n", + proc_files[j].name); + } + } +done: + if (ret == BT_STATUS_FAILURE) { + if (priv->dev_proc[seq].proc_entry) { + remove_proc_entry(m_dev->name, proc_mbt); + priv->dev_proc[seq].proc_entry = NULL; + } + if (priv->dev_proc[seq].pfiles) { + for (j = 0; j < priv->dev_proc[seq].num_proc_files; j++) { + if (priv->dev_proc[seq].pfiles[j].pdata) { + kfree(priv->dev_proc[seq].pfiles[j]. + pdata); + priv->dev_proc[seq].pfiles[j].pdata = + NULL; + } + } + kfree(priv->dev_proc[seq].pfiles); + priv->dev_proc[seq].pfiles = NULL; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function removes proc interface + * + * @param priv A pointer to bt_private structure + * @return N/A + */ +void +bt_proc_remove(bt_private *priv) +{ + int j, i; + char hist_entry[50]; + ENTER(); + PRINTM(INFO, "BT: Remove Proc Interface\n"); + if (proc_mbt) { + for (i = 0; i < MAX_RADIO_FUNC; i++) { + if (!priv->dev_proc[i].proc_entry) + continue; + for (j = 0; j < ARRAY_SIZE(proc_files); j++) { + remove_proc_entry(proc_files[j].name, + priv->dev_proc[i].proc_entry); + } + for (j = 0; j < MAX_ANTENNA_NUM; j++) { + snprintf(hist_entry, sizeof(hist_entry), + "bt-ant%d", j); + remove_proc_entry(hist_entry, + priv->dev_proc[i].hist_entry); + } + remove_proc_entry("histogram", + priv->dev_proc[i].proc_entry); + remove_proc_entry(priv->bt_dev.m_dev[i].name, proc_mbt); + priv->dev_proc[i].proc_entry = NULL; + + if (priv->dev_proc[i].pfiles) { + for (j = 0; + j < priv->dev_proc[i].num_proc_files; + j++) { + if (priv->dev_proc[i].pfiles[j].pdata) { + kfree(priv->dev_proc[i]. + pfiles[j].pdata); + priv->dev_proc[i].pfiles[j]. + pdata = NULL; + } + } + kfree(priv->dev_proc[i].pfiles); + priv->dev_proc[i].pfiles = NULL; + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function creates proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +bt_root_proc_init(void) +{ + PRINTM(INFO, "BT: Create Proc Interface\n"); + proc_mbt = proc_mkdir("mbt", PROC_DIR); + if (!proc_mbt) { + PRINTM(ERROR, "BT: Cannot create proc interface\n"); + return BT_STATUS_FAILURE; + } + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function removes proc interface + * directory structure + * + * @return BT_STATUS_SUCCESS + */ +int +bt_root_proc_remove(void) +{ + remove_proc_entry("mbt", PROC_DIR); + proc_mbt = NULL; + return BT_STATUS_SUCCESS; +}
diff --git a/bt_sd8997/bt_char/bt_sdio.h b/bt_sd8997/bt_char/bt_sdio.h new file mode 100644 index 0000000..ac3ac01 --- /dev/null +++ b/bt_sd8997/bt_char/bt_sdio.h
@@ -0,0 +1,258 @@ +/** @file bt_sdio.h + * @brief This file contains SDIO (interface) module + * related macros, enum, and structure. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _BT_SDIO_H_ +#define _BT_SDIO_H_ + +#include <linux/irqreturn.h> + +/** IRQ return type */ +typedef irqreturn_t IRQ_RET_TYPE; +/** IRQ return */ +#define IRQ_RET (return IRQ_HANDLED) +/** ISR notifier function */ +typedef IRQ_RET_TYPE (*isr_notifier_fn_t) (s32 irq, void *dev_id, + struct pt_regs * reg); + +/** SDIO header length */ +#define SDIO_HEADER_LEN 4 + +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 */ +#define GPIO_INT_NEW_MODE 255 +/* SD block size can not bigger than 64 due to buf size limit in firmware */ +/** define SD block size for data Tx/Rx */ +#define SD_BLOCK_SIZE 64 +/** define SD block size for firmware download */ +#define SD_BLOCK_SIZE_FW_DL 256 + +/** Number of blocks for firmware transfer */ +#define FIRMWARE_TRANSFER_NBLOCK 2 + +/** Firmware ready */ +#define FIRMWARE_READY 0xfedc + +/* Bus Interface Control Reg 0x07 */ +/** SD BUS width 1 */ +#define SD_BUS_WIDTH_1 0x00 +/** SD BUS width 4 */ +#define SD_BUS_WIDTH_4 0x02 +/** SD BUS width mask */ +#define SD_BUS_WIDTH_MASK 0x03 +/** Asynchronous interrupt mode */ +#define ASYNC_INT_MODE 0x20 + +/** magic register */ +#define CARD_MAGIC_REG 0xF0 +/** magic value */ +#define MAGIC_VAL 0x24 + +/* Host Control Registers */ +/** Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/** Host Control Registers : Host without Command 53 finish host*/ +#define HOST_TO_CARD_EVENT (0x1U << 3) +/** Host Control Registers : Host terminates Command 53 */ +#define HOST_TERM_CMD53 (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Host interrupt RSR */ +#define HOST_INT_RSR_REG 0x04 + +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) + +/** Host Control Registers : Host interrupt mask */ +#define HOST_INT_MASK_REG 0x08 + +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Enable Host interrupt mask */ +#define HIM_ENABLE (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +/** Host Control Registers : Host interrupt status */ +#define HOST_INTSTATUS_REG 0x0C +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Host Control Registers : Host Transfer status */ +#define HOST_INT_STATUS_REG 0x58 +/** Host Control Registers : Upload CRC error */ +#define UP_LD_CRC_ERR (0x1U << 2) +/** Host Control Registers : Upload restart */ +#define UP_LD_RESTART (0x1U << 1) +/** Host Control Registers : Download restart */ +#define DN_LD_RESTART (0x1U << 0) + +/** Card Control Registers : Card to Host Event register */ +#define CARD_STATUS_REG 0x5C +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host interrupt mask register */ +#define HOST_INTERRUPT_MASK_REG 0x60 +/** Card Control Registers : Host power interrupt mask */ +#define HOST_POWER_INT_MASK (0x1U << 3) +/** Card Control Registers : Abort card interrupt mask */ +#define ABORT_CARD_INT_MASK (0x1U << 2) +/** Card Control Registers : Upload card interrupt mask */ +#define UP_LD_CARD_INT_MASK (0x1U << 1) +/** Card Control Registers : Download card interrupt mask */ +#define DN_LD_CARD_INT_MASK (0x1U << 0) + +/** Card Control Registers : Card interrupt status register */ +#define CARD_INTERRUPT_STATUS_REG 0x64 +/** Card Control Registers : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** Card Control Registers : Card interrupt RSR register */ +#define CARD_INTERRUPT_RSR_REG 0x68 +/** Card Control Registers : Power up RSR */ +#define POWER_UP_RSR (0x1U << 4) +/** Card Control Registers : Power down RSR */ +#define POWER_DOWN_RSR (0x1U << 3) + +/* Card Control Registers */ +/** Card Control Registers : Read SQ base address A0 register */ +#define SQ_READ_BASE_ADDRESS_A0_REG 0xf8 +/** Card Control Registers : Read SQ base address A1 register */ +#define SQ_READ_BASE_ADDRESS_A1_REG 0xf9 +/** Card Control Registers : Read SQ base address A2 register */ +#define SQ_READ_BASE_ADDRESS_A2_REG 0x6E +/** Card Control Registers : Read SQ base address A3 register */ +#define SQ_READ_BASE_ADDRESS_A3_REG 0x6F +/** Card Control Registers : Write SQ base address A0 register */ +#define SQ_WRITE_BASE_ADDRESS_A0_REG 0x70 +/** Card Control Registers : Write SQ base address A1 register */ +#define SQ_WRITE_BASE_ADDRESS_A1_REG 0x71 +/** Card Control Registers : Write SQ base address A2 register */ +#define SQ_WRITE_BASE_ADDRESS_A2_REG 0x72 +/** Card Control Registers : Write SQ base address A3 register */ +#define SQ_WRITE_BASE_ADDRESS_A3_REG 0x73 + +/** Card Control Registers : Card revision register */ +#define CARD_REVISION_REG 0xC8 + +/** Firmware status 0 register (SCRATCH0_0) */ +#define CARD_FW_STATUS0_REG 0xe8 +/** Firmware status 1 register (SCRATCH0_1) */ +#define CARD_FW_STATUS1_REG 0xe9 +/** Rx length register (SCRATCH0_2) */ +#define CARD_RX_LEN_REG 0xea +/** Rx unit register (SCRATCH0_3) */ +#define CARD_RX_UNIT_REG 0xeb +/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/ +#define ENABLE_GPIO_1_INT_MODE 0x88 +/** Scratch reg 3 2 : Configure GPIO-1 INT*/ +#define SCRATCH_REG_32 0xEE + +/** Card Control Registers : Card OCR 0 register */ +#define CARD_OCR_0_REG 0xD4 +/** Card Control Registers : Card OCR 1 register */ +#define CARD_OCR_1_REG 0xD5 +/** Card Control Registers : Card OCR 3 register */ +#define CARD_OCR_3_REG 0xD6 +/** Card Control Registers : Card config register */ +#define CARD_CONFIG_REG 0xD7 +/** Card Control Registers : Miscellaneous Configuration Register */ +#define CARD_MISC_CFG_REG 0xD8 +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT (0x1U << 4) + +/** Card Control Registers : Debug 0 register */ +#define DEBUG_0_REG 0xDC +/** Card Control Registers : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : Debug 1 register */ +#define DEBUG_1_REG 0xDD +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : Debug 2 register */ +#define DEBUG_2_REG 0xDE +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : Debug 3 register */ +#define DEBUG_3_REG 0xDF +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Host Control Registers : I/O port 0 */ +#define IO_PORT_0_REG 0xE4 +/** Host Control Registers : I/O port 1 */ +#define IO_PORT_1_REG 0xE5 +/** Host Control Registers : I/O port 2 */ +#define IO_PORT_2_REG 0xE6 + +struct sdio_mmc_card { + /** sdio_func structure pointer */ + struct sdio_func *func; + /** bt_private structure pointer */ + bt_private *priv; +}; + +/** DMA alignment value */ +#define DMA_ALIGNMENT 64 +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) \ + (((p) + ((a) - 1)) & ~((a) - 1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** This function read cmd52 register */ +int sd_write_reg(bt_private *priv, int reg, u8 val); +/** This function write cmd52 value to register */ +int sd_read_reg(bt_private *priv, int reg, u8 *data); +/** This function reads the Cmd52 value in dev structure */ +int sd_read_cmd52_val(bt_private *priv); +/** This function updates card reg based on the Cmd52 value in dev structure */ +int sd_write_cmd52_val(bt_private *priv, int func, int reg, int val); + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** This function tells lower driver that BT is suspended */ +void bt_is_suspended(bt_private *priv); +#endif +#endif +#endif +#endif /* _BT_SDIO_H_ */
diff --git a/bt_sd8997/bt_char/bt_sdiommc.c b/bt_sd8997/bt_char/bt_sdiommc.c new file mode 100644 index 0000000..9867049 --- /dev/null +++ b/bt_sd8997/bt_char/bt_sdiommc.c
@@ -0,0 +1,1920 @@ +/** @file bt_sdiommc.c + * @brief This file contains SDIO IF (interface) module + * related functions. + * + * Copyright (C) 2007-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/firmware.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/card.h> + +#include "bt_drv.h" +#include "bt_sdio.h" + +/** define marvell vendor id */ +#define MARVELL_VENDOR_ID 0x02df + +/** Max retry number of CMD53 read/write */ +#define MAX_CMD53_RETRY 3 +/** Max retry number of CMD53 read/write */ +#define MAX_CMD52_RETRY 3 +/** Firmware name */ +static char *fw_name; +/** fw serial download flag */ +extern int bt_fw_serial; +/** request firmware nowait */ +int bt_req_fw_nowait; +static int multi_fn = BIT(2); + +#define SD8997_FW_NAME "mrvl/sdsd8997_combo_v4.bin" +#define SD8997_BT_FW_NAME "mrvl/sd8997_bt_v4.bin" +#define DEFAULT_FW_NAME "mrvl/sdsd8997_combo_v4.bin" +#define DEFAULT_BT_FW_NAME "mrvl/sd8997_bt_v4.bin" + +/** Function number 2 */ +#define FN2 2 +/** Device ID for SD8997 FN2 */ +#define SD_DEVICE_ID_8997_BT_FN2 0x9142 + +/** Array of SDIO device ids when multi_fn=0x12 */ +static const struct sdio_device_id bt_ids[] = { + {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997_BT_FN2)}, + {} +}; + +MODULE_DEVICE_TABLE(sdio, bt_ids); + +/******************************************************** + Global Variables +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +extern int mbt_pm_keep_power; +#endif + +extern bt_private *m_priv[]; +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function gets rx_unit value + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_get_rx_unit(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_unit_reg = CARD_RX_UNIT_REG; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_unit_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + priv->bt_dev.rx_unit = reg; + + LEAVE(); + return ret; +} + +/** + * @brief This function reads fwstatus registers + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_read_firmware_status(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 fws0; + u8 fws1; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = CARD_FW_STATUS0_REG; + u8 card_fw_status1_reg = CARD_FW_STATUS1_REG; + + ENTER(); + + fws0 = sdio_readb(card->func, card_fw_status0_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + fws1 = sdio_readb(card->func, card_fw_status1_reg, &ret); + if (ret < 0) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + *dat = (((u16) fws1) << 8) | fws0; + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function reads rx length + * + * @param priv A pointer to bt_private structure + * @param dat A pointer to keep returned data + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sd_read_rx_len(bt_private *priv, u16 * dat) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_rx_len_reg = CARD_RX_LEN_REG; + + ENTER(); + + reg = sdio_readb(card->func, card_rx_len_reg, &ret); + if (ret == BT_STATUS_SUCCESS) + *dat = (u16) reg << priv->bt_dev.rx_unit; + + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts mask + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_enable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = HOST_INT_MASK_REG; + + ENTER(); + + sdio_writeb(card->func, mask, host_int_mask_reg, &ret); + if (ret) { + PRINTM(WARN, "BT: Unable to enable the host interrupt!\n"); + ret = BT_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** @brief This function disables the host interrupts mask. + * + * @param priv A pointer to bt_private structure + * @param mask the interrupt mask + * @return BT_STATUS_SUCCESS or other error no. + */ +static int +sbi_disable_host_int_mask(bt_private *priv, u8 mask) +{ + int ret = BT_STATUS_FAILURE; + u8 host_int_mask; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 host_int_mask_reg = HOST_INT_MASK_REG; + + ENTER(); + + /* Read back the host_int_mask register */ + host_int_mask = sdio_readb(card->func, host_int_mask_reg, &ret); + if (ret) + goto done; + + /* Update with the mask and write back to the register */ + host_int_mask &= ~mask; + sdio_writeb(card->func, host_int_mask, host_int_mask_reg, &ret); + if (ret < 0) { + PRINTM(WARN, "BT: Unable to diable the host interrupt!\n"); + goto done; + } + ret = BT_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function polls the card status register + * + * @param priv A pointer to bt_private structure + * @param bits the bit mask + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_poll_card_status(bt_private *priv, u8 bits) +{ + int tries; + int rval; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 cs; + u8 card_status_reg = CARD_STATUS_REG; + + ENTER(); + + for (tries = 0; tries < MAX_POLL_TRIES * 1000; tries++) { + cs = sdio_readb(card->func, card_status_reg, &rval); + if (rval != 0) + break; + if (rval == 0 && (cs & bits) == bits) { + LEAVE(); + return BT_STATUS_SUCCESS; + } + udelay(1); + } + PRINTM(ERROR, + "BT: sdio_poll_card_status failed (%d), tries = %d, cs = 0x%x\n", + rval, tries, cs); + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_cmd52_val(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 func, reg, val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + func = priv->bt_dev.cmd52_func; + reg = priv->bt_dev.cmd52_reg; + sdio_claim_host(card->func); + if (func) + val = sdio_readb(card->func, reg, &ret); + else + val = sdio_f0_readb(card->func, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, "BT: Cannot read value from func %d reg %d\n", + func, reg); + } else { + priv->bt_dev.cmd52_val = val; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param func Stores func variable + * @param reg Stores reg variable + * @param val Stores val variable + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_cmd52_val(bt_private *priv, int func, int reg, int val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + + ENTER(); + + if (val >= 0) { + /* Perform actual write only if val is provided */ + sdio_claim_host(card->func); + if (func) + sdio_writeb(card->func, val, reg, &ret); + else + sdio_f0_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + if (ret) { + PRINTM(ERROR, + "BT: Cannot write value (0x%x) to func %d reg %d\n", + val, func, reg); + goto done; + } + priv->bt_dev.cmd52_val = val; + } + + /* Save current func and reg for future read */ + priv->bt_dev.cmd52_func = func; + priv->bt_dev.cmd52_reg = reg; + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function updates card reg based on the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to write + * @param val value + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_write_reg(bt_private *priv, int reg, u8 val) +{ + int ret = BT_STATUS_SUCCESS; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + sdio_writeb(card->func, val, reg, &ret); + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function reads updates the Cmd52 value in dev structure + * + * @param priv A pointer to bt_private structure + * @param reg register to read + * @param data Data + * @return BT_STATUS_SUCCESS or other error no. + */ +int +sd_read_reg(bt_private *priv, int reg, u8 *data) +{ + int ret = BT_STATUS_SUCCESS; + u8 val; + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + ENTER(); + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + *data = val; + LEAVE(); + return ret; +} + +/** + * @brief This function probes the card + * + * @param func A pointer to sdio_func structure. + * @param id A pointer to structure sdio_device_id + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_probe_card(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = NULL; + struct sdio_mmc_card *card = NULL; + + ENTER(); + + PRINTM(INFO, "BT: vendor=0x%x,device=0x%x,class=%d,fn=%d\n", id->vendor, + id->device, id->class, func->num); + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto done; + } + card->func = func; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + /* wait for chip fully wake up */ + if (!func->enable_timeout) + func->enable_timeout = 200; +#endif + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) { + sdio_disable_func(func); + sdio_release_host(func); + PRINTM(FATAL, "BT: sdio_enable_func() failed: ret=%d\n", ret); + kfree(card); + LEAVE(); + return -EIO; + } + sdio_release_host(func); + priv = bt_add_card(card); + if (!priv) { + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + ret = BT_STATUS_FAILURE; + kfree(card); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks if the firmware is ready to accept + * command or not. + * + * @param priv A pointer to bt_private structure + * @param pollnum Number of times to poll fw status + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_verify_fw_download(bt_private *priv, int pollnum) +{ + int ret = BT_STATUS_FAILURE; + u16 firmwarestat = 0; + int tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if (sd_read_firmware_status(priv, &firmwarestat) < 0) + continue; + if (firmwarestat == FIRMWARE_READY) { + PRINTM(MSG, "BT FW is active(%d)\n", tries); + ret = BT_STATUS_SUCCESS; + break; + } + mdelay(100); + } + if ((pollnum > 1) && (ret != BT_STATUS_SUCCESS)) { + PRINTM(ERROR, + "Fail to poll firmware status: firmwarestat=0x%x\n", + firmwarestat); + bt_dump_sdio_regs(priv); + } + LEAVE(); + return ret; +} + +/** + * @brief Transfers firmware to card + * + * @param priv A Pointer to bt_private structure + * @param fw A Pointer to fw image + * @param fw_len fw image len + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +static int +sd_init_fw_dpc(bt_private *priv, u8 *fw, int fw_len) +{ + struct sdio_mmc_card *card = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 *firmware = fw; + int firmwarelen = fw_len; + u8 base0; + u8 base1; + int ret = BT_STATUS_SUCCESS; + int offset; + void *tmpfwbuf = NULL; + int tmpfwbufsz; + u8 *fwbuf; + u16 len; + int txlen = 0; + int tx_blocks = 0; + int i = 0; + int tries = 0; +#ifdef FW_DOWNLOAD_SPEED + u32 tv1, tv2; +#endif + u8 sq_read_base_address_a0_reg = SQ_READ_BASE_ADDRESS_A0_REG; + u8 sq_read_base_address_a1_reg = SQ_READ_BASE_ADDRESS_A1_REG; + + ENTER(); + + PRINTM(INFO, "BT: Downloading FW image (%d bytes)\n", firmwarelen); + +#ifdef FW_DOWNLOAD_SPEED + tv1 = get_utimeofday(); +#endif + + tmpfwbufsz = BT_UPLD_SIZE + DMA_ALIGNMENT; + tmpfwbuf = kzalloc(tmpfwbufsz, GFP_KERNEL); + if (!tmpfwbuf) { + PRINTM(ERROR, + "BT: Unable to allocate buffer for firmware. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + /* Ensure aligned firmware buffer */ + fwbuf = (u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); + + /* Perform firmware data transfer */ + offset = 0; + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits + */ + ret = sd_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret < 0) { + PRINTM(FATAL, + "BT: FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + /* More data? */ + if (offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + base0 = sdio_readb(card->func, + sq_read_base_address_a0_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", + base0, base0); + ret = BT_STATUS_FAILURE; + goto done; + } + base1 = sdio_readb(card->func, + sq_read_base_address_a1_reg, &ret); + if (ret) { + PRINTM(WARN, "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download\n", + base1, base1); + ret = BT_STATUS_FAILURE; + goto done; + } + len = (((u16) base1) << 8) | base0; + + if (len != 0) + break; + udelay(10); + } + + if (len == 0) + break; + else if (len > BT_UPLD_SIZE) { + PRINTM(FATAL, + "BT: FW download failure @ %d, invalid length %d\n", + offset, len); + ret = BT_STATUS_FAILURE; + goto done; + } + /** ignore CRC check before download the first packet */ + if (offset == 0 && (len & BIT(0))) + len &= ~BIT(0); + txlen = len; + + if (len & BIT(0)) { + i++; + if (i >= MAX_CMD53_RETRY) { + PRINTM(FATAL, + "BT: FW download failure @ %d, over max retry count\n", + offset); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: FW CRC error indicated by the helper:" + " len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + + PRINTM(ERROR, "BT: retry: %d, offset %d\n", i, offset); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last block */ + if (firmwarelen - offset < txlen) + txlen = firmwarelen - offset; + + PRINTM(INFO, "."); + + tx_blocks = + (txlen + SD_BLOCK_SIZE_FW_DL - + 1) / SD_BLOCK_SIZE_FW_DL; + + /* Copy payload to buffer */ + memcpy(fwbuf, &firmware[offset], txlen); + } + + /* Send data */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, fwbuf, + tx_blocks * SD_BLOCK_SIZE_FW_DL); + + if (ret < 0) { + PRINTM(ERROR, + "BT: FW download, write iomem (%d) failed @ %d\n", + i, offset); + sdio_writeb(card->func, 0x04, CONFIGURATION_REG, &ret); + if (ret) + PRINTM(ERROR, "write ioreg failed (CFG)\n"); + } + + offset += txlen; + } while (TRUE); + + PRINTM(MSG, "BT: FW download over, size %d bytes\n", offset); + + ret = BT_STATUS_SUCCESS; +done: +#ifdef FW_DOWNLOAD_SPEED + tv2 = get_utimeofday(); + PRINTM(INFO, "FW: %d.%03d.%03d ", tv1 / 1000000, + (tv1 % 1000000) / 1000, tv1 % 1000); + PRINTM(INFO, " -> %d.%03d.%03d ", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); + tv2 -= tv1; + PRINTM(INFO, " == %d.%03d.%03d\n", tv2 / 1000000, + (tv2 % 1000000) / 1000, tv2 % 1000); +#endif + kfree(tmpfwbuf); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * + * @param fw_firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_request_fw_dpc(const struct firmware *fw_firmware, void *context) +{ + int ret = BT_STATUS_SUCCESS; + bt_private *priv = (bt_private *)context; + struct sdio_mmc_card *card = NULL; + struct m_dev *m_dev_bt = NULL; + struct timeval tstamp; + int index; + + ENTER(); + + m_dev_bt = &priv->bt_dev.m_dev[BT_SEQ]; + + if ((priv == NULL) || (priv->adapter == NULL) || + (priv->bt_dev.card == NULL) || (m_dev_bt == NULL) + ) { + LEAVE(); + return BT_STATUS_FAILURE; + } + + card = (struct sdio_mmc_card *)priv->bt_dev.card; + + if (!fw_firmware) { + do_gettimeofday(&tstamp); + if (tstamp.tv_sec > + (priv->req_fw_time.tv_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(ERROR, + "BT: No firmware image found. Skipping download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + PRINTM(ERROR, + "BT: No firmware image found! Retrying download\n"); + /* Wait a second here before calling the callback again */ + os_sched_timeout(1000); + sd_download_firmware_w_helper(priv); + LEAVE(); + return ret; + } + + priv->firmware = fw_firmware; + + if (BT_STATUS_FAILURE == + sd_init_fw_dpc(priv, (u8 *)priv->firmware->data, + priv->firmware->size)) { + PRINTM(ERROR, + "BT: sd_init_fw_dpc failed (download fw with nowait: %d). Terminating download\n", + bt_req_fw_nowait); + sdio_release_host(card->func); + ret = BT_STATUS_FAILURE; + goto done; + } + + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_FIRMWARE_POLL_TRIES)) { + PRINTM(ERROR, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + sdio_release_host(card->func); + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + LEAVE(); + return ret; + +done: + if (fw_firmware) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + if (!bt_req_fw_nowait) +#endif + release_firmware(fw_firmware); + } + /* For synchronous download cleanup will be done in add_card */ + if (!bt_req_fw_nowait) + return ret; + PRINTM(INFO, "unregister device\n"); + sbi_unregister_dev(priv); + ((struct sdio_mmc_card *)card)->priv = NULL; + /* Stop the thread servicing the interrupts */ + priv->adapter->SurpriseRemoved = TRUE; + wake_up_interruptible(&priv->MainThread.waitQ); + while (priv->MainThread.pid) + os_sched_timeout(1); + bt_proc_remove(priv); + clean_up_m_devs(priv); + bt_free_adapter(priv); + for (index = 0; index < MAX_BT_ADAPTER; index++) { + if (m_priv[index] == priv) { + m_priv[index] = NULL; + break; + } + } + bt_priv_put(priv); + LEAVE(); + return ret; +} + +/** + * @brief request_firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware structure + * @param context A Pointer to bt_private structure + * @return None + **/ +static void +sd_request_fw_callback(const struct firmware *firmware, void *context) +{ + ENTER(); + sd_request_fw_dpc(firmware, context); + LEAVE(); + return; +} + +/** + * @brief This function downloads firmware image to the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sd_download_firmware_w_helper(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + int err; + char *cur_fw_name = NULL; + + ENTER(); + + cur_fw_name = fw_name; + if (fw_name == NULL) { + if (!bt_fw_serial || priv->fw_reload || bt_fw_reload) + cur_fw_name = DEFAULT_BT_FW_NAME; + else + cur_fw_name = DEFAULT_FW_NAME; + } + + PRINTM(MSG, "BT Request firmware: %s\n", cur_fw_name); + if (bt_req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + GFP_KERNEL, priv, + sd_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#else + ret = request_firmware_nowait(THIS_MODULE, + cur_fw_name, priv->hotplug_device, + priv, sd_request_fw_callback); +#endif +#endif + if (ret < 0) + PRINTM(FATAL, + "BT: request_firmware_nowait() failed, error code = %#x\n", + ret); + } else { + err = request_firmware(&priv->firmware, cur_fw_name, + priv->hotplug_device); + if (err < 0) { + PRINTM(FATAL, + "BT: request_firmware() failed, error code = %#x\n", + err); + ret = BT_STATUS_FAILURE; + } else + ret = sd_request_fw_dpc(priv->firmware, priv); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function reads data from the card. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +static int +sd_card_to_host(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u16 buf_len = 0; + int buf_block_len; + int blksz; + struct sk_buff *skb = NULL; + u32 type; + u8 *payload = NULL; + struct mbt_dev *mbt_dev = NULL; + struct m_dev *mdev_bt = &(priv->bt_dev.m_dev[BT_SEQ]); + struct m_dev *mdev_debug = &(priv->bt_dev.m_dev[DEBUG_SEQ]); + struct debug_dev *debug_dev = + (struct debug_dev *)priv->bt_dev.m_dev[DEBUG_SEQ].dev_pointer; + struct sdio_mmc_card *card = priv->bt_dev.card; + int i = 0; + + ENTER(); + if (priv->bt_dev.m_dev[BT_SEQ].spec_type != BLUEZ_SPEC) + mbt_dev = + (struct mbt_dev *)priv->bt_dev.m_dev[BT_SEQ]. + dev_pointer; + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + do { + /* Read the length of data to be transferred */ + ret = sd_read_rx_len(priv, &buf_len); + if (ret < 0) { + i++; + PRINTM(ERROR, "BT: Read scratch reg failed (%d)\n", i); + if (i >= MAX_CMD52_RETRY) { + ret = BT_STATUS_FAILURE; + goto exit; + } + udelay(20); + } + } + while (ret == BT_STATUS_FAILURE); + + /* Allocate buffer */ + blksz = SD_BLOCK_SIZE; + buf_block_len = (buf_len + blksz - 1) / blksz; + if (buf_len <= BT_HEADER_LEN || + (buf_block_len * blksz) > ALLOC_BUF_SIZE) { + PRINTM(ERROR, "BT: card_to_host, invalid packet length: %d\n", + buf_len); + ret = BT_STATUS_FAILURE; + goto exit; + } + skb = bt_skb_alloc(buf_block_len * blksz + DMA_ALIGNMENT, GFP_ATOMIC); + if (skb == NULL) { + PRINTM(WARN, "BT: No free skb\n"); + goto exit; + } + if ((t_ptr)skb->data & (DMA_ALIGNMENT - 1)) { + skb_put(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + skb_pull(skb, + DMA_ALIGNMENT - + ((t_ptr)skb->data & (DMA_ALIGNMENT - 1))); + } + + payload = skb->data; + i = 0; + do { + ret = sdio_readsb(card->func, payload, priv->bt_dev.ioport, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: card_to_host, read iomem (%d) failed: %d\n", + i, ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) { + kfree_skb(skb); + skb = NULL; + goto exit; + } + } + } while (ret == BT_STATUS_FAILURE); + /* This is SDIO specific header length: byte[2][1][0], * type: byte[3] + (HCI_COMMAND = 1, ACL_DATA = 2, SCO_DATA = 3, 0xFE = Vendor) */ + buf_len = payload[0]; + buf_len |= (u16) payload[1] << 8; + type = payload[3]; + PRINTM(DATA, "BT: SDIO Blk Rd %s: len=%d type=%d\n", mbt_dev->name, + buf_len, type); + if (buf_len > buf_block_len * blksz) { + PRINTM(ERROR, + "BT: Drop invalid rx pkt, len in hdr=%d, cmd53 length=%d\n", + buf_len, buf_block_len * blksz); + ret = BT_STATUS_FAILURE; + kfree_skb(skb); + skb = NULL; + goto exit; + } + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Rd", payload, buf_len); + switch (type) { + case HCI_ACLDATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (*(u16 *) skb->data == 0xffff) { + skb_queue_tail(&priv->adapter->fwdump_queue, skb); + break; + } + bt_recv_frame(priv, skb); + break; + case HCI_SCODATA_PKT: + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + bt_recv_frame(priv, skb); + break; + case HCI_EVENT_PKT: + /** add EVT Demux */ + bt_cb(skb)->pkt_type = type; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS == check_evtpkt(priv, skb)) + break; + switch (skb->data[0]) { + case 0x0E: + /** cmd complete */ + if (priv->debug_device_pending) { + if (priv->debug_ocf_ogf[0] == skb->data[3] && + priv->debug_ocf_ogf[1] == skb->data[4]) { + priv->debug_device_pending = 0; + priv->debug_ocf_ogf[0] = 0; + priv->debug_ocf_ogf[1] = 0; + /** debug cmd complete */ + if (debug_dev) { + skb->dev = (void *)mdev_debug; + mdev_recv_frame(skb); + mdev_debug->stat.byte_rx += + buf_len; + } + break; + } + } + bt_recv_frame(priv, skb); + break; + case 0x0F: + /** cmd status */ + bt_recv_frame(priv, skb); + break; + case 0xFF: + /** Vendor specific pkt */ + bt_recv_frame(priv, skb); + break; + default: + bt_recv_frame(priv, skb); + break; + } + break; + case MRVL_VENDOR_PKT: + /* Just think here need to back compatible FM */ + bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; + skb_put(skb, buf_len); + skb_pull(skb, BT_HEADER_LEN); + if (BT_STATUS_SUCCESS != bt_process_event(priv, skb)) + bt_recv_frame(priv, skb); + break; + default: + /* Driver specified event and command resp should be handle + here */ + PRINTM(INFO, "BT: Unknown PKT type:%d\n", type); + kfree_skb(skb); + skb = NULL; + break; + } +exit: + if (ret) { + if (mbt_dev) + mdev_bt->stat.err_rx++; + PRINTM(ERROR, "error when recv pkt!\n"); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function removes the card + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_remove_card(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + card = sdio_get_drvdata(func); + if (card) { + bt_remove_card(card->priv); + kfree(card); + } + } + + LEAVE(); +} + +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to sdio_func structure + * @return N/A + */ +static void +sd_interrupt(struct sdio_func *func) +{ + bt_private *priv; + struct m_dev *m_dev = NULL; + struct sdio_mmc_card *card; + int ret = BT_STATUS_SUCCESS; + u8 ireg = 0; + u8 host_intstatus_reg = HOST_INTSTATUS_REG; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->priv) { + PRINTM(INFO, + "BT: %s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", + __func__, func, card); + LEAVE(); + return; + } + priv = card->priv; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + ret = sdio_readsb(card->func, priv->adapter->hw_regs, 0, SD_BLOCK_SIZE); + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: cmd53 read int status register failed %d\n", + ret); + goto done; + } + ireg = priv->adapter->hw_regs[host_intstatus_reg]; + if (ret) { + PRINTM(ERROR, + "BT: sdio_read_ioreg: CMD52 read int status register failed %d\n", + ret); + goto done; + } + if (ireg != 0) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * Clear the interrupt status register and re-enable + * the interrupt + */ + PRINTM(INTR, "BT: INT %s: sdio_ireg = 0x%x\n", m_dev->name, + ireg); + priv->adapter->irq_recv = ireg; + } else { + PRINTM(ERROR, "BT: ERR: ireg=0\n"); + } + OS_INT_DISABLE; + priv->adapter->sd_ireg |= ireg; + OS_INT_RESTORE; + bt_interrupt(m_dev); +done: + LEAVE(); +} + +/** + * @brief This function checks if the interface is ready to download + * or not while other download interfaces are present + * + * @param priv A pointer to bt_private structure + * @param val Winner status (0: winner) + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sd_check_winner_status(bt_private *priv, u8 *val) +{ + + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)priv->bt_dev.card; + u8 card_fw_status0_reg = CARD_FW_STATUS0_REG; + + ENTER(); + winner = sdio_readb(cardp->func, card_fw_status0_reg, &ret); + if (ret != BT_STATUS_SUCCESS) { + LEAVE(); + return BT_STATUS_FAILURE; + } + *val = winner; + + LEAVE(); + return ret; +} + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** @brief This function tells lower driver that BT is suspended + * + * @param priv A pointer to bt_private structure + * @return None + */ +void +bt_is_suspended(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + priv->adapter->is_suspended = TRUE; + sdio_func_suspended(card->func); +} +#endif + +/** @brief This function handles client driver suspend + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS or other error no. + */ +int +bt_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: suspend: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + return -ENOSYS; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO suspend\n", m_dev->name); + mbt_hci_suspend_dev(m_dev); + skb_queue_purge(&priv->adapter->tx_queue); + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, "BT: HS not actived, suspend fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to suspend!\n"); + } + } + } + + priv->adapter->is_suspended = TRUE; + + LEAVE(); + /* We will keep the power when hs enabled successfully */ + if ((mbt_pm_keep_power) && (priv->adapter->hs_state == HS_ACTIVATED)) { +#ifdef MMC_PM_SKIP_RESUME_PROBE + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER and " + "MMC_PM_SKIP_RESUME_PROBE\n"); + return sdio_set_host_pm_flags(func, + MMC_PM_KEEP_POWER | + MMC_PM_SKIP_RESUME_PROBE); +#else + PRINTM(CMD, "BT: suspend with MMC_PM_KEEP_POWER\n"); + return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +#endif + } else { + PRINTM(CMD, "BT: suspend without MMC_PM_KEEP_POWER\n"); + return BT_STATUS_SUCCESS; + } +} + +void +bt_sdio_shutdown(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + + ENTER(); + + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: shutdown: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + if (!(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(ERROR, + "BT: %s: cannot remain alive while host is shutdown\n", + sdio_func_id(func)); + return; + } + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return; + } + + priv = cardp->priv; + + if ((mbt_pm_keep_power) && (priv->adapter->hs_state != HS_ACTIVATED)) { +#ifdef BLE_WAKEUP + /** Set BLE Wake up pattern */ + if (BT_STATUS_SUCCESS != bt_config_ble_wakeup(priv)) + PRINTM(ERROR, "BT: Set ble wakeup pattern fail!\n"); +#endif + + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, "BT: HS not actived, shutdown fail!\n"); + if (BT_STATUS_SUCCESS != bt_enable_hs(priv)) { + PRINTM(CMD, + "BT: HS not actived the second time, force to shutdown!\n"); + } + } + } + LEAVE(); +} + +/** @brief This function handles client driver resume + * + * @param dev A pointer to device structure + * @return BT_STATUS_SUCCESS + */ +int +bt_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + bt_private *priv = NULL; + struct sdio_mmc_card *cardp; + struct m_dev *m_dev = NULL; + + ENTER(); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(CMD, "BT: %s: resume: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->priv) { + PRINTM(ERROR, "BT: Card or priv structure is not valid\n"); + LEAVE(); + return BT_STATUS_SUCCESS; + } + + priv = cardp->priv; + priv->adapter->is_suspended = FALSE; + m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); + PRINTM(CMD, "BT %s: SDIO resume\n", m_dev->name); +#ifdef BLE_WAKEUP + if (priv->ble_wakeup_buf) { + PRINTM(CMD, "BT: Send system resume event\n"); + bt_send_system_event(priv, FALSE); + } +#endif + mbt_hci_resume_dev(m_dev); + sbi_wakeup_firmware(priv); + priv->adapter->hs_state = HS_DEACTIVATED; + PRINTM(CMD, "BT:%s: HS DEACTIVATED in Resume!\n", m_dev->name); + LEAVE(); + return BT_STATUS_SUCCESS; +} +#endif +#endif + +/******************************************************** + Global Functions +********************************************************/ +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +static const struct dev_pm_ops bt_sdio_pm_ops = { + .suspend = bt_sdio_suspend, + .resume = bt_sdio_resume, +}; +#endif +#endif +static struct sdio_driver sdio_bt = { + .name = "sdio_bt", + .id_table = bt_ids, + .probe = sd_probe_card, + .remove = sd_remove_card, +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &bt_sdio_pm_ops, + .shutdown = bt_sdio_shutdown, + } +#endif +#endif +}; + +/** + * @brief This function registers the bt module in bus driver. + * + * @return An int pointer that keeps returned value + */ +int * +sbi_register(void) +{ + int *ret; + + ENTER(); + + if (sdio_register_driver(&sdio_bt) != 0) { + PRINTM(FATAL, "BT: SD Driver Registration Failed\n"); + LEAVE(); + return NULL; + } else + ret = (int *)1; + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the bt module in bus driver. + * + * @return N/A + */ +void +sbi_unregister(void) +{ + ENTER(); + sdio_unregister_driver(&sdio_bt); + LEAVE(); +} + +/** + * @brief This function registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_register_dev(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 reg; + u8 chiprev; + struct sdio_mmc_card *card = priv->bt_dev.card; + struct sdio_func *func; + u8 host_intstatus_reg = HOST_INTSTATUS_REG; + u8 host_int_rsr_reg = HOST_INT_RSR_REG; + u8 card_misc_cfg_reg = CARD_MISC_CFG_REG; + u8 card_revision_reg = CARD_REVISION_REG; + u8 io_port_0_reg = IO_PORT_0_REG; + u8 io_port_1_reg = IO_PORT_1_REG; + u8 io_port_2_reg = IO_PORT_2_REG; + u8 card_magic_reg = CARD_MAGIC_REG; + u8 magic_val = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: Error: card or function is NULL!\n"); + goto failed; + } + func = card->func; + priv->hotplug_device = &func->dev; + + /* Initialize the private structure */ + strncpy(priv->bt_dev.name, "bt_sdio0", sizeof(priv->bt_dev.name)); + priv->bt_dev.ioport = 0; + priv->bt_dev.fn = func->num; + + sdio_claim_host(func); + ret = sdio_claim_irq(func, sd_interrupt); + if (ret) { + PRINTM(FATAL, ": sdio_claim_irq failed: ret=%d\n", ret); + goto release_host; + } + ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); + if (ret) { + PRINTM(FATAL, ": %s: cannot set SDIO block size\n", __func__); + goto release_irq; + } + + /* read Revision Register to get the chip revision number */ + chiprev = sdio_readb(func, card_revision_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_REVISION_REG\n"); + goto release_irq; + } + priv->adapter->chip_rev = chiprev; + PRINTM(INFO, "revision=%#x\n", chiprev); + + magic_val = sdio_readb(func, card_magic_reg, &ret); + if (ret) { + PRINTM(FATAL, ": cannot read CARD_MAGIC_REG\n"); + goto release_irq; + } + priv->adapter->magic_val = magic_val; + PRINTM(INFO, "magic_val=%#x\n", magic_val); + + /* + * Read the HOST_INTSTATUS_REG for ACK the first interrupt got + * from the bootloader. If we don't do this we get a interrupt + * as soon as we register the irq. + */ + reg = sdio_readb(func, host_intstatus_reg, &ret); + if (ret < 0) + goto release_irq; + + /* Read the IO port */ + reg = sdio_readb(func, io_port_0_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= reg; + + reg = sdio_readb(func, io_port_1_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 8); + + reg = sdio_readb(func, io_port_2_reg, &ret); + if (ret < 0) + goto release_irq; + else + priv->bt_dev.ioport |= (reg << 16); + + PRINTM(INFO, ": SDIO FUNC%d IO port: 0x%x\n", priv->bt_dev.fn, + priv->bt_dev.ioport); + +#define SDIO_INT_MASK 0x3F + /* Set Host interrupt reset to read to clear */ + reg = sdio_readb(func, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | SDIO_INT_MASK, host_int_rsr_reg, &ret); + if (ret < 0) + goto release_irq; + /* Set auto re-enable */ + reg = sdio_readb(func, card_misc_cfg_reg, &ret); + if (ret < 0) + goto release_irq; + sdio_writeb(func, reg | AUTO_RE_ENABLE_INT, card_misc_cfg_reg, &ret); + if (ret < 0) + goto release_irq; + + sdio_set_drvdata(func, card); + sdio_release_host(func); + + LEAVE(); + return BT_STATUS_SUCCESS; +release_irq: + sdio_release_irq(func); +release_host: + sdio_release_host(func); +failed: + + LEAVE(); + return BT_STATUS_FAILURE; +} + +/** + * @brief This function de-registers the device. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_unregister_dev(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + if (card && card->func) { + sdio_claim_host(card->func); + sdio_release_irq(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + sdio_set_drvdata(card->func, NULL); + } + + LEAVE(); + return BT_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_enable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sd_enable_host_int_mask(priv, HIM_ENABLE); + sd_get_rx_unit(priv); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_disable_host_int(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret; + + ENTER(); + + if (!card || !card->func) { + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + ret = sbi_disable_host_int_mask(priv, HIM_DISABLE); + sdio_release_host(card->func); + + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param priv A pointer to bt_private structure + * @param payload A pointer to the data/cmd buffer + * @param nb Length of data/cmd + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_host_to_card(bt_private *priv, u8 *payload, u16 nb) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; +#ifdef DEBUG_LEVEL1 + struct m_dev *m_dev = &(priv->bt_dev.m_dev[BT_SEQ]); +#endif + int ret = BT_STATUS_SUCCESS; + int buf_block_len; + int blksz; + int i = 0; + u8 *buf = NULL; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + buf = payload; + + blksz = SD_BLOCK_SIZE; + buf_block_len = (nb + blksz - 1) / blksz; + /* Allocate buffer and copy payload */ + if ((t_ptr)payload & (DMA_ALIGNMENT - 1)) { + if (nb > MAX_TX_BUF_SIZE) { + PRINTM(ERROR, "BT: Invalid tx packet, size=%d\n", nb); + LEAVE(); + return BT_STATUS_FAILURE; + } + /* Ensure 8-byte aligned CMD buffer */ + buf = priv->adapter->tx_buf; + memcpy(buf, payload, nb); + } + sdio_claim_host(card->func); + do { + /* Transfer data to card */ + ret = sdio_writesb(card->func, priv->bt_dev.ioport, buf, + buf_block_len * blksz); + if (ret < 0) { + i++; + PRINTM(ERROR, + "BT: host_to_card, write iomem (%d) failed: %d\n", + i, ret); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0 */ + sdio_f0_writeb(card->func, 0x01, SDIO_CCCR_ABORT, &ret); +#endif + sdio_writeb(card->func, HOST_TERM_CMD53, + CONFIGURATION_REG, &ret); + udelay(20); + ret = BT_STATUS_FAILURE; + if (i >= MAX_CMD53_RETRY) + goto exit; + } else { + PRINTM(DATA, "BT: SDIO Blk Wr %s: len=%d\n", + m_dev->name, nb); + DBG_HEXDUMP(DAT_D, "BT: SDIO Blk Wr", payload, nb); + } + } while (ret == BT_STATUS_FAILURE); + priv->bt_dev.tx_dnld_rdy = FALSE; +exit: + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function downloads firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS or BT_STATUS_FAILURE + */ +int +sbi_download_fw(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + u8 winner = 0; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + ret = BT_STATUS_FAILURE; + goto exit; + } + + sdio_claim_host(card->func); + if (BT_STATUS_SUCCESS == sd_verify_fw_download(priv, 1)) { + PRINTM(MSG, "BT: FW already downloaded!\n"); + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + /* Check if other interface is downloading */ + ret = sd_check_winner_status(priv, &winner); + if (ret == BT_STATUS_FAILURE) { + PRINTM(FATAL, "BT read winner status failed!\n"); + goto done; + } + if (winner) { + PRINTM(MSG, "BT is not the winner (0x%x). Skip FW download\n", + winner); + /* check if the fimware is downloaded successfully or not */ + if (sd_verify_fw_download(priv, MAX_MULTI_INTERFACE_POLL_TRIES)) { + PRINTM(FATAL, "BT: FW failed to be active in time!\n"); + ret = BT_STATUS_FAILURE; + goto done; + } + sdio_release_host(card->func); + sbi_enable_host_int(priv); + if (BT_STATUS_FAILURE == sbi_register_conf_dpc(priv)) { + PRINTM(ERROR, + "BT: sbi_register_conf_dpc failed. Terminating download\n"); + ret = BT_STATUS_FAILURE; + goto err_register; + } + goto exit; + } + + do_gettimeofday(&priv->req_fw_time); + /* Download the main firmware via the helper firmware */ + if (sd_download_firmware_w_helper(priv)) { + PRINTM(INFO, "BT: FW download failed!\n"); + ret = BT_STATUS_FAILURE; + } + goto exit; +done: + sdio_release_host(card->func); +exit: + LEAVE(); + return ret; +err_register: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the interrupt status and handle it accordingly. + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS + */ +int +sbi_get_int_status(bt_private *priv) +{ + int ret = BT_STATUS_SUCCESS; + u8 sdio_ireg = 0; + struct sdio_mmc_card *card = priv->bt_dev.card; + + ENTER(); + + OS_INT_DISABLE; + sdio_ireg = priv->adapter->sd_ireg; + priv->adapter->sd_ireg = 0; + OS_INT_RESTORE; + sdio_claim_host(card->func); + priv->adapter->irq_done = sdio_ireg; + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ + if (priv->bt_dev.tx_dnld_rdy) { /* tx_done already received */ + PRINTM(INFO, + "BT: warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", + priv->bt_dev.tx_dnld_rdy, sdio_ireg); + } else { + priv->bt_dev.tx_dnld_rdy = TRUE; + } + } + if (sdio_ireg & UP_LD_HOST_INT_STATUS) + sd_card_to_host(priv); + + ret = BT_STATUS_SUCCESS; + sdio_release_host(card->func); + LEAVE(); + return ret; +} + +/** + * @brief This function wakeup firmware + * + * @param priv A pointer to bt_private structure + * @return BT_STATUS_SUCCESS/BT_STATUS_FAILURE or other error no. + */ +int +sbi_wakeup_firmware(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + + ENTER(); + + if (!card || !card->func) { + PRINTM(ERROR, "BT: card or function is NULL!\n"); + LEAVE(); + return BT_STATUS_FAILURE; + } + sdio_claim_host(card->func); + sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); + sdio_release_host(card->func); + PRINTM(CMD, "BT wake up firmware\n"); + + LEAVE(); + return ret; +} + +#define INIT_START_REG 0xF1 +#define INIT_END_REG 0xF6 + +/** @brief This function dump the SDIO register + * + * @param priv A Pointer to the bt_private structure + * + * @return N/A + */ +void +bt_dump_sdio_regs(bt_private *priv) +{ + struct sdio_mmc_card *card = priv->bt_dev.card; + int ret = BT_STATUS_SUCCESS; + char buf[256], *ptr; + u8 loop, func, data; + unsigned int reg, reg_start, reg_end; + u8 loop_num = 2; + unsigned int init_reg_start = 0; + unsigned int init_reg_end = 0; + init_reg_start = INIT_START_REG; + init_reg_end = INIT_END_REG; + + if (priv->adapter->ps_state) + sbi_wakeup_firmware(priv); + + sdio_claim_host(card->func); + for (loop = 0; loop < loop_num; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else { + func = 2; + reg_start = 0; + reg_end = 0x09; + } + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, reg_start, + reg_end); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(card->func, reg, &ret); + else + data = sdio_readb(card->func, reg, &ret); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + + if (init_reg_start) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "Init Status Reg (%#x-%#x): ", + init_reg_start, init_reg_end); + for (reg = init_reg_start; reg <= init_reg_end;) { + data = sdio_readb(card->func, reg, &ret); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + reg++; + } + PRINTM(MSG, "%s\n", buf); + } + sdio_release_host(card->func); +} + +module_param(fw_name, charp, 0); +MODULE_PARM_DESC(fw_name, "Firmware name"); +module_param(bt_req_fw_nowait, int, 0); +MODULE_PARM_DESC(bt_req_fw_nowait, + "0: Use request_firmware API; 1: Use request_firmware_nowait API"); +module_param(multi_fn, int, 0); +MODULE_PARM_DESC(multi_fn, "Bit 2: FN2;");
diff --git a/bt_sd8997/bt_char/hci_wrapper.h b/bt_sd8997/bt_char/hci_wrapper.h new file mode 100644 index 0000000..3fe16b6 --- /dev/null +++ b/bt_sd8997/bt_char/hci_wrapper.h
@@ -0,0 +1,171 @@ +/** @file hci_wrapper.h + * @brief This file contains HCI related definitions + * + * Copyright (C) 2011-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _HCI_WRAPPER_H_ +#define _HCI_WRAPPER_H_ + +#include <linux/module.h> +#include <net/bluetooth/bluetooth.h> +#include <net/bluetooth/hci_core.h> + +/** Define Seq num */ +#define BT_SEQ 0 +#define DEBUG_SEQ 3 + +/** Define dev type */ +#define BT_TYPE 1 +#define BT_AMP_TYPE 2 +#define DEBUG_TYPE 5 + +/** Define spec type */ +#define BLUEZ_SPEC 1 +#define IANYWHERE_SPEC 2 +#define GENERIC_SPEC 3 + +/** Define lock/unlock wrapper */ +#define mdev_req_lock(d) down(&d->req_lock) +#define mdev_req_unlock(d) up(&d->req_lock) + +/** Length of device name */ +#define DEV_NAME_LEN 32 + +/** Define struct m_dev */ +struct m_dev { + char name[DEV_NAME_LEN]; + int index; + unsigned long flags; + spinlock_t lock; + struct semaphore req_lock; + struct sk_buff_head rx_q; + wait_queue_head_t req_wait_q; + struct hci_dev_stats stat; + struct module *owner; + void *dev_pointer; + int dev_type; + int spec_type; + void *driver_data; + int read_continue_flag; + int wait_rx_complete; + int rx_complete_flag; + wait_queue_head_t rx_wait_q; + spinlock_t rxlock; + atomic_t extra_cnt; + + struct sk_buff *evt_skb; + struct sk_buff *acl_skb; + struct sk_buff *sco_skb; + + int (*open) (struct m_dev * m_dev); + int (*close) (struct m_dev * m_dev); + int (*flush) (struct m_dev * m_dev); + int (*send) (struct m_dev * m_dev, struct sk_buff * skb); + void (*destruct) (struct m_dev * m_dev); + void (*notify) (struct m_dev * m_dev, unsigned int evt); + int (*ioctl) (struct m_dev * m_dev, unsigned int cmd, void *arg); + void (*query) (struct m_dev * m_dev, void *arg); + +}; + +/** Define struct mbt_dev */ +struct mbt_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; + __u8 type; + + __u16 pkt_type; + __u16 esco_type; + __u16 link_policy; + __u16 link_mode; + + __u32 idle_timeout; + __u16 sniff_min_interval; + __u16 sniff_max_interval; + + struct sk_buff *reassembly[3]; + + atomic_t promisc; +}; + +struct debug_dev { + /** maybe could add some private member later */ + char name[DEV_NAME_LEN]; + unsigned long flags; +}; + +/** This function frees m_dev allocation */ +void free_m_dev(struct m_dev *m_dev); + +/** + * @brief This function receives frames + * + * @param skb A pointer to struct sk_buff + * @return 0--success otherwise error code + */ +static inline int +mdev_recv_frame(struct sk_buff *skb) +{ + struct m_dev *m_dev = (struct m_dev *)skb->dev; + if (!m_dev || (!test_bit(HCI_UP, &m_dev->flags) + && !test_bit(HCI_INIT, &m_dev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Incomming skb */ + bt_cb(skb)->incoming = 1; + + /* Time stamp */ + __net_timestamp(skb); + + /* Queue frame for rx task */ + skb_queue_tail(&m_dev->rx_q, skb); + + /* Wakeup rx thread */ + wake_up_interruptible(&m_dev->req_wait_q); + + return 0; +} + +/** + * @brief mbt dev suspend handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_suspend_dev(struct m_dev *m_dev) +{ + return 0; +} + +/** + * @brief mbt dev resume handler + * + * @param m_dev A pointer to struct m_dev + * @return 0 + */ +static inline int +mbt_hci_resume_dev(struct m_dev *m_dev) +{ + return 0; +} + +#endif /* _HCI_WRAPPER_H_ */
diff --git a/bt_sd8997/bt_char/mbt_char.c b/bt_sd8997/bt_char/mbt_char.c new file mode 100644 index 0000000..b0c39ac --- /dev/null +++ b/bt_sd8997/bt_char/mbt_char.c
@@ -0,0 +1,807 @@ +/** @file mbt_char.c + * + * @brief This file contains the char device function calls + * + * Copyright (C) 2010-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include <linux/path.h> +#include <linux/namei.h> +#include <linux/mount.h> + +#include "bt_drv.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include <linux/sched/signal.h> +#endif +#include "mbt_char.h" + +static LIST_HEAD(char_dev_list); + +static DEFINE_SPINLOCK(char_dev_list_lock); + +static int mbtchar_major = MBTCHAR_MAJOR_NUM; + +/** + * @brief Gets char device structure + * + * @param dev A pointer to char_dev + * + * @return kobject structure + */ +struct kobject * +chardev_get(struct char_dev *dev) +{ + struct kobject *kobj; + + kobj = bt_priv_get(dev->m_dev->driver_data); + if (!kobj) + return NULL; + PRINTM(INFO, "dev get kobj\n"); + kobj = kobject_get(&dev->kobj); + if (!kobj) + bt_priv_put(dev->m_dev->driver_data); + return kobj; +} + +/** + * @brief Prints char device structure + * + * @param dev A pointer to char_dev + * + * @return N/A + */ +void +chardev_put(struct char_dev *dev) +{ + if (dev) { + struct m_dev *m_dev = dev->m_dev; + PRINTM(INFO, "dev put kobj\n"); + kobject_put(&dev->kobj); + if (m_dev) + bt_priv_put(m_dev->driver_data); + } +} + +/** + * @brief Changes permissions of the dev + * + * @param name pointer to character + * @param mode mode_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chmod(char *name, mode_t mode) +{ + struct path path; + struct inode *inode; + struct iattr newattrs; + int ret; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chmod(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief Changes ownership of the dev + * + * @param name pointer to character + * @param user uid_t type data + * @param group gid_t type data + * @return 0--success otherwise failure + */ +int +mbtchar_chown(char *name, uid_t user, gid_t group) +{ + struct path path; + struct inode *inode = NULL; + struct iattr newattrs; + int ret = 0; + int retrycount = 0; + + ENTER(); + do { + os_sched_timeout(30); + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (++retrycount >= 10) { + PRINTM(ERROR, + "mbtchar_chown(): fail to get kern_path\n"); + LEAVE(); + return -EFAULT; + } + } while (ret); + inode = path.dentry->d_inode; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_lock(&inode->i_mutex); +#else + inode_lock(inode); +#endif + ret = mnt_want_write(path.mnt); + if (ret) + goto out_unlock; + newattrs.ia_valid = ATTR_CTIME; + if (user != (uid_t) (-1)) { + newattrs.ia_valid |= ATTR_UID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_uid = user; +#else + newattrs.ia_uid = KUIDT_INIT(user); +#endif + } + if (group != (gid_t) (-1)) { + newattrs.ia_valid |= ATTR_GID; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + newattrs.ia_gid = group; +#else + newattrs.ia_gid = KGIDT_INIT(group); +#endif + } + if (!S_ISDIR(inode->i_mode)) + newattrs.ia_valid |= + ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + if (inode->i_op->setattr) + ret = inode->i_op->setattr(path.dentry, &newattrs); + else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + ret = simple_setattr(path.dentry, &newattrs); +#else + ret = inode_setattr(inode, &newattrs); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + + path_put(&path); + LEAVE(); + return ret; +out_unlock: +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,7,0) + mutex_unlock(&inode->i_mutex); +#else + inode_unlock(inode); +#endif + mnt_drop_write(path.mnt); + path_put(&path); + return ret; +} + +/** + * @brief write handler for char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes written + */ +ssize_t +chardev_write(struct file * filp, const char *buf, size_t count, loff_t * f_pos) +{ + int nwrite = 0; + struct sk_buff *skb; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + if (!test_bit(HCI_UP, &m_dev->flags)) { + LEAVE(); + return -EBUSY; + } + nwrite = count; + skb = bt_skb_alloc(count, GFP_ATOMIC); + if (!skb) { + PRINTM(ERROR, "mbtchar_write(): fail to alloc skb\n"); + LEAVE(); + return -ENOMEM; + } + + if (copy_from_user((void *)skb_put(skb, count), buf, count)) { + PRINTM(ERROR, "mbtchar_write(): cp_from_user failed\n"); + kfree_skb(skb); + nwrite = -EFAULT; + goto exit; + } + + skb->dev = (void *)m_dev; + bt_cb(skb)->pkt_type = *((unsigned char *)skb->data); + skb_pull(skb, 1); + + PRINTM(DATA, "Write: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + DBG_HEXDUMP(DAT_D, "chardev_write", skb->data, skb->len); + + /* Send skb to the hci wrapper layer */ + if (m_dev->send(m_dev, skb)) { + PRINTM(ERROR, "Write: Fail\n"); + nwrite = 0; + /* Send failed */ + kfree_skb(skb); + } +exit: + LEAVE(); + return nwrite; +} + +/** + * @brief read handler for BT char dev + * + * @param filp pointer to structure file + * @param buf pointer to char buffer + * @param count size of receive buffer + * @param f_pos pointer to loff_t type data + * @return number of bytes read + */ +ssize_t +chardev_read(struct file * filp, char *buf, size_t count, loff_t * f_pos) +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + /* Wait for rx data */ + add_wait_queue(&m_dev->req_wait_q, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + skb = skb_dequeue(&m_dev->rx_q); + if (skb) + break; + if (!test_bit(HCI_UP, &m_dev->flags)) { + ret = -EBUSY; + break; + } + + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -EINTR; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&m_dev->req_wait_q, &wait); + + if (!skb) + goto out; + + if (m_dev->read_continue_flag == 0) { + /* Put type byte before the data */ + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + PRINTM(DATA, "Read: pkt_type: 0x%x, len=%d @%lu\n", + bt_cb(skb)->pkt_type, skb->len, jiffies); + } + DBG_HEXDUMP(DAT_D, "chardev_read", skb->data, skb->len); + if (skb->len > count) { + /* user data length is smaller than the skb length */ + if (copy_to_user(buf, skb->data, count)) { + ret = -EFAULT; + goto outf; + } + skb_pull(skb, count); + skb_queue_head(&m_dev->rx_q, skb); + m_dev->read_continue_flag = 1; + wake_up_interruptible(&m_dev->req_wait_q); + ret = count; + goto out; + } else { + if (copy_to_user(buf, skb->data, skb->len)) { + ret = -EFAULT; + goto outf; + } + m_dev->read_continue_flag = 0; + ret = skb->len; + } +outf: + kfree_skb(skb); +out: + if (m_dev->wait_rx_complete && skb_queue_empty(&m_dev->rx_q)) { + m_dev->rx_complete_flag = TRUE; + wake_up_interruptible(&m_dev->rx_wait_q); + } + LEAVE(); + return ret; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl common handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +char_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, void *arg) +#else +/** + * @brief ioctl common handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +char_ioctl(struct file *filp, unsigned int cmd, void *arg) +#endif +{ + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; + PRINTM(INFO, "IOCTL: cmd=%d\n", cmd); + switch (cmd) { + case MBTCHAR_IOCTL_RELEASE: + m_dev->close(m_dev); + break; + case MBTCHAR_IOCTL_QUERY_TYPE: + m_dev->query(m_dev, arg); + break; + default: + m_dev->ioctl(m_dev, cmd, arg); + break; + } + LEAVE(); + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, (void *)arg); +#else + return char_ioctl(filp, cmd, (void *)arg); +#endif +} + +#ifdef CONFIG_COMPAT +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) +/** + * @brief compat ioctl handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +int +chardev_ioctl_compat(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +#else +/** + * @brief compat ioctl handler for char dev + * + * @param filp pointer to structure file + * @param cmd contains the IOCTL + * @param arg contains the arguement + * @return 0--success otherwise failure + */ +long +chardev_ioctl_compat(struct file *filp, unsigned int cmd, unsigned long arg) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + return char_ioctl(inode, filp, cmd, compat_ptr(arg)); +#else + return char_ioctl(filp, cmd, compat_ptr(arg)); +#endif +} +#endif /* CONFIG_COMPAT */ + +/** + * @brief open handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = NULL; + struct m_dev *m_dev = NULL; + struct char_dev *cdev = NULL; + struct list_head *p = NULL; + ENTER(); + + list_for_each(p, &char_dev_list) { + cdev = list_entry(p, struct char_dev, list); + if (mbtchar_major == MAJOR(inode->i_cdev->dev) && + cdev->minor == MINOR(inode->i_cdev->dev)) { + dev = cdev; + break; + } + } + if (!dev) { + PRINTM(ERROR, "cannot find dev from inode\n"); + LEAVE(); + return -ENXIO; + } + if (!chardev_get(dev)) { + LEAVE(); + return -ENXIO; + } + filp->private_data = dev; /* for other methods */ + m_dev = dev->m_dev; + mdev_req_lock(m_dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (test_bit(HCI_UP, &m_dev->flags)) { + atomic_inc(&m_dev->extra_cnt); + goto done; + } +#endif + if (m_dev->open(m_dev)) { + ret = -EIO; + goto done; + } + set_bit(HCI_UP, &m_dev->flags); + +done: + mdev_req_unlock(m_dev); + if (ret) + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief release handler for char dev + * + * @param inode pointer to structure inode + * @param filp pointer to structure file + * @return 0--success otherwise failure + */ +int +chardev_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev) { + LEAVE(); + return -ENXIO; + } + m_dev = dev->m_dev; +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + if (m_dev && (atomic_dec_if_positive(&m_dev->extra_cnt) >= 0)) { + LEAVE(); + return ret; + } +#endif + if (m_dev) + ret = dev->m_dev->close(dev->m_dev); + filp->private_data = NULL; + chardev_put(dev); + LEAVE(); + return ret; +} + +/** + * @brief poll handler for char dev + * + * @param filp pointer to structure file + * @param wait pointer to poll_table structure + * @return mask + */ +static unsigned int +chardev_poll(struct file *filp, poll_table * wait) +{ + unsigned int mask; + struct char_dev *dev = (struct char_dev *)filp->private_data; + struct m_dev *m_dev = NULL; + ENTER(); + if (!dev || !dev->m_dev) { + LEAVE(); + return -ENXIO; + } + + m_dev = dev->m_dev; + poll_wait(filp, &m_dev->req_wait_q, wait); + mask = POLLOUT | POLLWRNORM; + if (skb_peek(&m_dev->rx_q)) + mask |= POLLIN | POLLRDNORM; + if (!test_bit(HCI_UP, &(m_dev->flags))) + mask |= POLLHUP; + PRINTM(INFO, "poll mask=0x%x\n", mask); + LEAVE(); + return mask; +} + +/* File ops for the Char driver */ +const struct file_operations chardev_fops = { + .owner = THIS_MODULE, + .read = chardev_read, + .write = chardev_write, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + .ioctl = chardev_ioctl, +#else + .unlocked_ioctl = chardev_ioctl, +#endif +#ifdef CONFIG_COMPAT + .compat_ioctl = chardev_ioctl_compat, +#endif + .open = chardev_open, + .release = chardev_release, + .poll = chardev_poll, +}; + +/** + * @brief This function creates the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param mod_name A pointer to char + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name) +{ + int ret = 0, dev_num; + unsigned long flags; + ENTER(); + /* create the chrdev region */ + if (mbtchar_major) { + dev_num = MKDEV(mbtchar_major, dev->minor); + ret = register_chrdev_region(dev_num, 1, mod_name); + } else { + PRINTM(INFO, "chardev: no major # yet\n"); + ret = alloc_chrdev_region((dev_t *) & dev_num, dev->minor, 1, + mod_name); + } + + if (ret) { + PRINTM(ERROR, "chardev: create chrdev_region failed\n"); + LEAVE(); + return ret; + } + if (!mbtchar_major) { + /* Store the allocated dev major # */ + mbtchar_major = MAJOR(dev_num); + } + dev->cdev = cdev_alloc(); + dev->cdev->ops = &chardev_fops; + dev->cdev->owner = chardev_fops.owner; + dev_num = MKDEV(mbtchar_major, dev->minor); + + if (cdev_add(dev->cdev, dev_num, 1)) { + PRINTM(ERROR, "chardev: cdev_add failed\n"); + ret = -EFAULT; + goto free_cdev_region; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } + if (dev->dev_type == DEBUG_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), NULL, dev_name); + } +#else + if ((dev->dev_type == BT_TYPE) || (dev->dev_type == BT_AMP_TYPE)) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } + if (dev->dev_type == DEBUG_TYPE) { + device_create(char_class, NULL, + MKDEV(mbtchar_major, dev->minor), dev_name); + } +#endif + PRINTM(INFO, "register char dev=%s\n", dev_name); + + /** modify later */ + + spin_lock_irqsave(&char_dev_list_lock, flags); + list_add_tail(&dev->list, &char_dev_list); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + + LEAVE(); + return ret; +free_cdev_region: + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + LEAVE(); + return ret; +} + +/** + * @brief This function deletes the char dev + * + * @param dev A pointer to structure char_dev + * @param char_class A pointer to class struct + * @param dev_name A pointer to char + * @return 0--success otherwise failure + */ +int +unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name) +{ + ENTER(); + device_destroy(char_class, MKDEV(mbtchar_major, dev->minor)); + cdev_del(dev->cdev); + unregister_chrdev_region(MKDEV(mbtchar_major, dev->minor), 1); + PRINTM(INFO, "unregister char dev=%s\n", dev_name); + + LEAVE(); + return 0; +} + +/** + * @brief This function cleans module + * + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup(struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + do { + dev = NULL; + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + unregister_char_dev(dev, char_class, dev->m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } while (dev); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + class_destroy(char_class); + LEAVE(); +} + +/** + * @brief This function cleans module + * + * @param m_dev A pointer to m_dev struct + * @param char_class A pointer to class struct + * @return N/A + */ +void +chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class) +{ + unsigned long flags; + struct list_head *p = NULL; + struct char_dev *dev = NULL; + ENTER(); + spin_lock_irqsave(&char_dev_list_lock, flags); + list_for_each(p, &char_dev_list) { + dev = list_entry(p, struct char_dev, list); + if (dev->minor == m_dev->index) { + list_del(p); + spin_unlock_irqrestore(&char_dev_list_lock, flags); + dev->m_dev = NULL; + unregister_char_dev(dev, char_class, m_dev->name); + kobject_put(&dev->kobj); + spin_lock_irqsave(&char_dev_list_lock, flags); + break; + } + } + spin_unlock_irqrestore(&char_dev_list_lock, flags); + LEAVE(); +}
diff --git a/bt_sd8997/bt_char/mbt_char.h b/bt_sd8997/bt_char/mbt_char.h new file mode 100644 index 0000000..9997a38 --- /dev/null +++ b/bt_sd8997/bt_char/mbt_char.h
@@ -0,0 +1,72 @@ +/** @file mbt_char.h + * + * @brief This file contains mbtchar driver specific defines etc + * + * Copyright (C) 2010-2018, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +#ifndef __MBT_CHAR_H__ +#define __MBT_CHAR_H__ + +#include <linux/cdev.h> +#include <linux/device.h> + +/** Define ioctl */ +#define MBTCHAR_IOCTL_RELEASE _IO('M', 1) +#define MBTCHAR_IOCTL_QUERY_TYPE _IO('M', 2) +#ifdef BLE_WAKEUP +#define MBTCHAR_IOCTL_BLE_WAKEUP_PARAM _IO('M', 4) +#endif + +#define MBTCHAR_MAJOR_NUM (0) + +/** Interface specific macros */ +#define MBTCHAR_MINOR_BASE (0) +#define FMCHAR_MINOR_BASE (10) +#define NFCCHAR_MINOR_BASE (20) +#define DEBUGCHAR_MINOR_BASE (30) + +/** Declaration of char_dev struct */ +struct char_dev { + struct list_head list; + int minor; + int dev_type; + struct cdev *cdev; + struct m_dev *m_dev; + struct kobject kobj; +}; + +/** Changes permissions of the dev */ +int mbtchar_chmod(char *name, mode_t mode); + +/** Changes ownership of the dev */ +int mbtchar_chown(char *name, uid_t user, gid_t group); + +/** This function creates the char dev */ +int register_char_dev(struct char_dev *dev, struct class *char_class, + char *mod_name, char *dev_name); + +/** This function deletes the char dev */ +int unregister_char_dev(struct char_dev *dev, struct class *char_class, + char *dev_name); + +/** This function cleans module */ +void chardev_cleanup(struct class *char_class); + +/** This function cleans module */ +void chardev_cleanup_one(struct m_dev *m_dev, struct class *char_class); + +#endif /*__MBT_CHAR_H__*/
diff --git a/wlan_sd8897/Makefile b/wlan_sd8897/Makefile new file mode 100644 index 0000000..f64c670 --- /dev/null +++ b/wlan_sd8897/Makefile
@@ -0,0 +1,596 @@ +# File: Makefile +# +# Copyright (C) 2008-2018, Marvell International Ltd. +# +# This software file (the "File") is distributed by Marvell International +# Ltd. under the terms of the GNU General Public License Version 2, June 1991 +# (the "License"). You may use, redistribute and/or modify this File in +# accordance with the terms and conditions of the License, a copy of which +# is available by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the +# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. +# +# A copy of the GPL is available in file gpl-2.0.txt accompanying in this +# deliverables. +# +# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE +# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE +# ARE EXPRESSLY DISCLAIMED. The License provides additional details about +# this warranty disclaimer. + +COMPATDIR=/lib/modules/$(KERNELVERSION_X86)/build/compat-wireless-3.2-rc1-1/include +CC= $(CROSS_COMPILE)gcc -I$(COMPATDIR) +LD= $(CROSS_COMPILE)ld +BACKUP= /root/backup +YMD= `date +%Y%m%d%H%M` + +ifneq ($(COMPAT_VERSION_CODE),) +DRV_DIR ?= $(shell pwd) +export DRV_DIR +COMPAT_VERSION=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$1}') +COMPAT_PATCHLEVEL=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$2}') +COMPAT_SUBLEVEL=$(shell echo $(COMPAT_VERSION_CODE) | awk -F '.' '{print $$3}') +DECL_HEADER_FILE=$(DRV_DIR)/mlinux/moal_main.h +$(shell sed -i 's/COMPAT_VERSION_CODE KERNEL_VERSION.*/COMPAT_VERSION_CODE KERNEL_VERSION(\ + $(COMPAT_VERSION), $(COMPAT_PATCHLEVEL), $(COMPAT_SUBLEVEL))/g' $(DECL_HEADER_FILE)) +endif + +############################################################################# +# Configuration Options +############################################################################# + +# Debug Option +# DEBUG LEVEL n/1/2: +# n: NO DEBUG +# 1: Only PRINTM(MMSG,...), PRINTM(MFATAL,...), ... +# 2: All PRINTM() +CONFIG_DEBUG=1 + +# Proc debug file +CONFIG_PROC_DEBUG=y + +# Enable STA mode support +CONFIG_STA_SUPPORT=y + +# Enable uAP mode support +CONFIG_UAP_SUPPORT=y + +# Enable WIFIDIRECT support +CONFIG_WIFI_DIRECT_SUPPORT=y + +# Enable WIFIDISPLAY support +CONFIG_WIFI_DISPLAY_SUPPORT=y + +# Re-association in driver +CONFIG_REASSOCIATION=y + + +# Manufacturing firmware support +CONFIG_MFG_CMD_SUPPORT=y + +# OpenWrt support +CONFIG_OPENWRT_SUPPORT=n + +# Big-endian platform +CONFIG_BIG_ENDIAN=n + + + + +ifeq ($(CONFIG_DRV_EMBEDDED_SUPPLICANT), y) +CONFIG_EMBEDDED_SUPP_AUTH=y +else +ifeq ($(CONFIG_DRV_EMBEDDED_AUTHENTICATOR), y) +CONFIG_EMBEDDED_SUPP_AUTH=y +endif +endif + +# Enable SDIO multi-port Tx aggregation +CONFIG_SDIO_MULTI_PORT_TX_AGGR=y + +# Enable SDIO multi-port Rx aggregation +CONFIG_SDIO_MULTI_PORT_RX_AGGR=y + +# SDIO suspend/resume +CONFIG_SDIO_SUSPEND_RESUME=y + +# DFS testing support +CONFIG_DFS_TESTING_SUPPORT=y + +# Multi-channel support +CONFIG_MULTI_CHAN_SUPPORT=y + + +# Use static link for app build +export CONFIG_STATIC_LINK=y +CONFIG_ANDROID_KERNEL=y + +#32bit app over 64bit kernel support +CONFIG_USERSPACE_32BIT_OVER_KERNEL_64BIT=y + + +############################################################################# +# Select Platform Tools +############################################################################# + +MODEXT = ko +ccflags-y += -I$(srctree)/drivers/soc/berlin/modules/wlan_sd8897/mlan +ccflags-y += -DLINUX + + + +ARCH ?= arm +KERNELDIR ?= /usr/src/arm/linux-4.1.43-bg4ct +CROSS_COMPILE ?= /usr/local/gcc-linaro-5.3-2016.02-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu- + +LD += -S + +BINDIR = ../bin_sd8897 +APPDIR= $(shell if test -d "mapp"; then echo mapp; fi) + +############################################################################# +# Compiler Flags +############################################################################# + + ccflags-y += -I$(KERNELDIR)/include + + ccflags-y += -DFPNUM='"68"' + +ifeq ($(CONFIG_DEBUG),1) + ccflags-y += -DDEBUG_LEVEL1 +endif + +ifeq ($(CONFIG_DEBUG),2) + ccflags-y += -DDEBUG_LEVEL1 + ccflags-y += -DDEBUG_LEVEL2 + DBG= -dbg +endif + +ifeq ($(CONFIG_PROC_DEBUG),y) + ccflags-y += -DPROC_DEBUG + export CONFIG_PROC_DEBUG +endif + +ifeq ($(CONFIG_64BIT), y) + ccflags-y += -DMLAN_64BIT +endif + +ifeq ($(CONFIG_STA_SUPPORT),y) + ccflags-y += -DSTA_SUPPORT +ifeq ($(CONFIG_REASSOCIATION),y) + ccflags-y += -DREASSOCIATION +endif +else +CONFIG_WIFI_DIRECT_SUPPORT=n +CONFIG_WIFI_DISPLAY_SUPPORT=n +CONFIG_STA_WEXT=n +CONFIG_STA_CFG80211=n +endif + +ifeq ($(CONFIG_UAP_SUPPORT),y) + ccflags-y += -DUAP_SUPPORT +else +CONFIG_WIFI_DIRECT_SUPPORT=n +CONFIG_WIFI_DISPLAY_SUPPORT=n +CONFIG_UAP_WEXT=n +CONFIG_UAP_CFG80211=n +endif + +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + ccflags-y += -DWIFI_DIRECT_SUPPORT +endif +ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y) + ccflags-y += -DWIFI_DISPLAY_SUPPORT +endif + +ifeq ($(CONFIG_MFG_CMD_SUPPORT),y) + ccflags-y += -DMFG_CMD_SUPPORT +endif + +ifeq ($(CONFIG_BIG_ENDIAN),y) + ccflags-y += -DBIG_ENDIAN_SUPPORT +endif + +ifeq ($(CONFIG_USERSPACE_32BIT_OVER_KERNEL_64BIT),y) + ccflags-y += -DUSERSPACE_32BIT_OVER_KERNEL_64BIT +endif + +ifeq ($(CONFIG_SDIO_MULTI_PORT_TX_AGGR),y) + ccflags-y += -DSDIO_MULTI_PORT_TX_AGGR +endif + +ifeq ($(CONFIG_SDIO_MULTI_PORT_RX_AGGR),y) + ccflags-y += -DSDIO_MULTI_PORT_RX_AGGR +endif + +ifeq ($(CONFIG_SDIO_SUSPEND_RESUME),y) + ccflags-y += -DSDIO_SUSPEND_RESUME +endif + +ifeq ($(CONFIG_MULTI_CHAN_SUPPORT),y) + ccflags-y += -DMULTI_CHAN_SUPPORT +endif + +ifeq ($(CONFIG_DFS_TESTING_SUPPORT),y) + ccflags-y += -DDFS_TESTING_SUPPORT +endif + + +ifeq ($(CONFIG_ANDROID_KERNEL), y) + ccflags-y += -DANDROID_KERNEL +endif + +ifeq ($(CONFIG_OPENWRT_SUPPORT), y) + ccflags-y += -DOPENWRT +endif + + +ifeq ($(CONFIG_T50), y) + ccflags-y += -DT50 + ccflags-y += -DT40 + ccflags-y += -DT3T +endif + +# add -Wno-packed-bitfield-compat when GCC version greater than 4.4 +GCC_VERSION := $(shell echo `gcc -dumpversion | cut -f1-2 -d.` \>= 4.4 | sed -e 's/\./*100+/g' | bc ) +ifeq ($(GCC_VERSION),1) + ccflags-y += -Wno-packed-bitfield-compat +endif + +############################################################################# +# Make Targets +############################################################################# + +ifneq ($(KERNELRELEASE),) + +ifeq ($(CONFIG_WIRELESS_EXT),y) +ifeq ($(CONFIG_WEXT_PRIV),y) + # Enable WEXT for STA + CONFIG_STA_WEXT=y + # Enable WEXT for uAP + CONFIG_UAP_WEXT=y +else +# Disable WEXT for STA + CONFIG_STA_WEXT=n +# Disable WEXT for uAP + CONFIG_UAP_WEXT=n +endif +endif + +# Enable CFG80211 for STA +ifeq ($(CONFIG_CFG80211),y) + CONFIG_STA_CFG80211=y +else +ifeq ($(CONFIG_CFG80211),m) + CONFIG_STA_CFG80211=y +else + CONFIG_STA_CFG80211=n +endif +endif + +# OpenWrt +ifeq ($(CONFIG_OPENWRT_SUPPORT), y) +ifeq ($(CPTCFG_CFG80211),y) + CONFIG_STA_CFG80211=y +else +ifeq ($(CPTCFG_CFG80211),m) + CONFIG_STA_CFG80211=y +else + CONFIG_STA_CFG80211=n +endif +endif +endif + +# Enable CFG80211 for uAP +ifeq ($(CONFIG_CFG80211),y) + CONFIG_UAP_CFG80211=y +else +ifeq ($(CONFIG_CFG80211),m) + CONFIG_UAP_CFG80211=y +else + CONFIG_UAP_CFG80211=n +endif +endif + +# OpenWrt +ifeq ($(CONFIG_OPENWRT_SUPPORT), y) +ifeq ($(CPTCFG_CFG80211),y) + CONFIG_UAP_CFG80211=y +else +ifeq ($(CPTCFG_CFG80211),m) + CONFIG_UAP_CFG80211=y +else + CONFIG_UAP_CFG80211=n +endif +endif +endif + +ifneq ($(CONFIG_STA_SUPPORT),y) + CONFIG_WIFI_DIRECT_SUPPORT=n + CONFIG_WIFI_DISPLAY_SUPPORT=n + CONFIG_STA_WEXT=n + CONFIG_STA_CFG80211=n +endif + +ifneq ($(CONFIG_UAP_SUPPORT),y) + CONFIG_WIFI_DIRECT_SUPPORT=n + CONFIG_WIFI_DISPLAY_SUPPORT=n + CONFIG_UAP_WEXT=n + CONFIG_UAP_CFG80211=n +endif + +ifeq ($(CONFIG_STA_SUPPORT),y) +ifeq ($(CONFIG_STA_WEXT),y) + ccflags-y += -DSTA_WEXT +endif +ifeq ($(CONFIG_STA_CFG80211),y) + ccflags-y += -DSTA_CFG80211 +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +ifeq ($(CONFIG_UAP_WEXT),y) + ccflags-y += -DUAP_WEXT +endif +ifeq ($(CONFIG_UAP_CFG80211),y) + ccflags-y += -DUAP_CFG80211 +endif +endif + +print: +ifeq ($(CONFIG_STA_SUPPORT),y) +ifeq ($(CONFIG_STA_WEXT),n) +ifeq ($(CONFIG_STA_CFG80211),n) + @echo "Can not build STA without WEXT or CFG80211" + exit 2 +endif +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +ifeq ($(CONFIG_UAP_WEXT),n) +ifeq ($(CONFIG_UAP_CFG80211),n) + @echo "Can not build UAP without WEXT or CFG80211" + exit 2 +endif +endif +endif + + + + + +MOALOBJS = mlinux/moal_main.o \ + mlinux/moal_ioctl.o \ + mlinux/moal_shim.o \ + mlinux/moal_eth_ioctl.o + +MLANOBJS = mlan/mlan_shim.o mlan/mlan_init.o \ + mlan/mlan_txrx.o \ + mlan/mlan_cmdevt.o mlan/mlan_misc.o \ + mlan/mlan_cfp.o \ + mlan/mlan_module.o + +MLANOBJS += mlan/mlan_wmm.o +MLANOBJS += mlan/mlan_sdio.o +MLANOBJS += mlan/mlan_11n_aggr.o +MLANOBJS += mlan/mlan_11n_rxreorder.o +MLANOBJS += mlan/mlan_11n.o +MLANOBJS += mlan/mlan_11ac.o +MLANOBJS += mlan/mlan_11d.o +MLANOBJS += mlan/mlan_11h.o +ifeq ($(CONFIG_STA_SUPPORT),y) +MLANOBJS += mlan/mlan_meas.o +MLANOBJS += mlan/mlan_scan.o \ + mlan/mlan_sta_ioctl.o \ + mlan/mlan_sta_rx.o \ + mlan/mlan_sta_tx.o \ + mlan/mlan_sta_event.o \ + mlan/mlan_sta_cmd.o \ + mlan/mlan_sta_cmdresp.o \ + mlan/mlan_join.o +ifeq ($(CONFIG_STA_WEXT),y) +MOALOBJS += mlinux/moal_priv.o \ + mlinux/moal_wext.o +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +MLANOBJS += mlan/mlan_uap_ioctl.o +MLANOBJS += mlan/mlan_uap_cmdevent.o +MLANOBJS += mlan/mlan_uap_txrx.o +MOALOBJS += mlinux/moal_uap.o +ifeq ($(CONFIG_UAP_WEXT),y) +MOALOBJS += mlinux/moal_uap_priv.o +MOALOBJS += mlinux/moal_uap_wext.o +endif +endif +ifeq ($(CONFIG_STA_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.o +MOALOBJS += mlinux/moal_cfgvendor.o +MOALOBJS += mlinux/moal_sta_cfg80211.o +endif +ifeq ($(CONFIG_UAP_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.o +MOALOBJS += mlinux/moal_cfgvendor.o +MOALOBJS += mlinux/moal_uap_cfg80211.o +endif + +ifdef CONFIG_PROC_FS +MOALOBJS += mlinux/moal_proc.o +ifeq ($(CONFIG_PROC_DEBUG),y) +MOALOBJS += mlinux/moal_debug.o +endif +endif + + + + +ifeq ($(CONFIG_MULTI_INTERFACE),y) +obj-m := mlan_sdio.o +mlan_sdio-objs := $(MLANOBJS) +else +obj-m := mlan.o +mlan-objs := $(MLANOBJS) +endif + +obj-m := 8897mlan.o +8897mlan-objs := $(MLANOBJS) +MOALOBJS += mlinux/moal_sdio_mmc.o +obj-m += sd8897.o +sd8897-objs := $(MOALOBJS) + +# Otherwise we were called directly from the command line; invoke the kernel build system. +else + +default: + $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules + +endif + +############################################################### + +export CC LD ccflags-y KERNELDIR + +ifeq ($(CONFIG_STA_SUPPORT),y) +ifeq ($(CONFIG_UAP_SUPPORT),y) +.PHONY: mapp/mlanconfig mapp/mlan2040coex mapp/mlanevent mapp/uaputl mapp/mlanutl clean distclean +else +.PHONY: mapp/mlanconfig mapp/mlanevent mapp/mlan2040coex mapp/mlanutl clean distclean +endif +else +ifeq ($(CONFIG_UAP_SUPPORT),y) +.PHONY: mapp/mlanevent mapp/uaputl clean distclean +endif +endif + @echo "Finished Making Marvell Wlan Linux Driver" + +ifeq ($(CONFIG_STA_SUPPORT),y) +mapp/mlanconfig: + $(MAKE) -C $@ +mapp/mlanutl: + $(MAKE) -C $@ +mapp/mlan2040coex: + $(MAKE) -C $@ +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +mapp/uaputl: + $(MAKE) -C $@ +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) +mapp/wifidirectutl: + $(MAKE) -C $@ +endif +mapp/mlanevent: + $(MAKE) -C $@ + +echo: + +build: echo default + + @if [ ! -d $(BINDIR) ]; then \ + mkdir $(BINDIR); \ + fi + +ifeq ($(CONFIG_MULTI_INTERFACE),y) + cp -f mlan_sdio.$(MODEXT) $(BINDIR)/mlan_sdio$(DBG).$(MODEXT) +else + cp -f mlan.$(MODEXT) $(BINDIR)/mlan$(DBG).$(MODEXT) +endif + cp -f sd8xxx.$(MODEXT) $(BINDIR)/sd8897$(DBG).$(MODEXT) + +ifeq ($(CONFIG_STA_SUPPORT),y) + cp -f README $(BINDIR) + cp -f README_MLAN $(BINDIR) + cp -f README_RBC $(BINDIR) +ifeq ($(CONFIG_OPENWRT_SUPPORT),y) + cp -f README_OPENWRT $(BINDIR) +endif +ifneq ($(APPDIR),) + $(MAKE) -C mapp/mlanconfig $@ INSTALLDIR=$(BINDIR) + $(MAKE) -C mapp/mlanutl $@ INSTALLDIR=$(BINDIR) + $(MAKE) -C mapp/mlan2040coex $@ INSTALLDIR=$(BINDIR) +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) + cp -f README_UAP $(BINDIR) +ifneq ($(APPDIR),) + $(MAKE) -C mapp/uaputl $@ INSTALLDIR=$(BINDIR) +endif +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + cp -f README_WIFIDIRECT $(BINDIR) + cp -rpf script/wifidirect $(BINDIR) +ifeq ($(CONFIG_WIFI_DISPLAY_SUPPORT),y) + cp -rpf script/wifidisplay $(BINDIR) +endif +ifneq ($(APPDIR),) + $(MAKE) -C mapp/wifidirectutl $@ INSTALLDIR=$(BINDIR) +endif +endif +ifneq ($(APPDIR),) + $(MAKE) -C mapp/mlanevent $@ INSTALLDIR=$(BINDIR) +endif + +clean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name "Module.symvers" -exec rm {} \; + -find . -name "Module.markers" -exec rm {} \; + -find . -name "modules.order" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions +ifneq ($(APPDIR),) +ifeq ($(CONFIG_STA_SUPPORT),y) + $(MAKE) -C mapp/mlanconfig $@ + $(MAKE) -C mapp/mlanutl $@ + $(MAKE) -C mapp/mlan2040coex $@ +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) + $(MAKE) -C mapp/uaputl $@ +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + $(MAKE) -C mapp/wifidirectutl $@ +endif + $(MAKE) -C mapp/mlanevent $@ +endif + +install: default + + cp -f mlan.$(MODEXT) $(INSTALLDIR)/mlan$(DBG).$(MODEXT) + cp -f ../io/sdio/$(PLATFORM)/sdio.$(MODEXT) $(INSTALLDIR) + cp -f sd8xxx.$(MODEXT) $(INSTALLDIR)/sd8897$(DBG).$(MODEXT) + echo "sd8897 Driver Installed" + +distclean: + -find . -name "*.o" -exec rm {} \; + -find . -name "*.orig" -exec rm {} \; + -find . -name "*.swp" -exec rm {} \; + -find . -name "*.*~" -exec rm {} \; + -find . -name "*~" -exec rm {} \; + -find . -name "*.d" -exec rm {} \; + -find . -name "*.a" -exec rm {} \; + -find . -name "tags" -exec rm {} \; + -find . -name ".*" -exec rm -rf 2> /dev/null \; + -find . -name "*.ko" -exec rm {} \; + -find . -name ".*.cmd" -exec rm {} \; + -find . -name "*.mod.c" -exec rm {} \; + -find . -name ".*.dwo" -exec rm {} \; + -find . -name "*dwo" -exec rm {} \; + -rm -rf .tmp_versions +ifneq ($(APPDIR),) +ifeq ($(CONFIG_STA_SUPPORT),y) + $(MAKE) -C mapp/mlanconfig $@ + $(MAKE) -C mapp/mlanutl $@ + $(MAKE) -C mapp/mlan2040coex $@ +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) + $(MAKE) -C mapp/uaputl $@ +endif +ifeq ($(CONFIG_WIFI_DIRECT_SUPPORT),y) + $(MAKE) -C mapp/wifidirectutl $@ +endif + $(MAKE) -C mapp/mlanevent $@ +endif + +# End of file
diff --git a/wlan_sd8897/README b/wlan_sd8897/README new file mode 100644 index 0000000..73e44e0 --- /dev/null +++ b/wlan_sd8897/README
@@ -0,0 +1,2365 @@ +=============================================================================== + U S E R M A N U A L + + Copyright (C) 2008-2018, Marvell International Ltd. + All Rights Reserved + +1) FOR DRIVER BUILD + + Goto source code directory wlan_src/. + make [clean] build + The driver and utility binaries can be found in ../bin_xxxx directory. + The driver code supports Linux kernel up to 4.15. + +2) FOR DRIVER INSTALL + + a) Copy firmware image sd8786_uapsta.bin | sd8787_uapsta.bin | ... to + /lib/firmware/mrvl/ directory, create the directory if it doesn't exist. + b) Install WLAN driver, + There are drv_mode, max_sta_bss, max_uap_bss etc. module parameters. + The bit settings of drv_mode are, + Bit 0 : STA + Bit 1 : uAP + Bit 2 : WIFIDIRECT + The default drv_mode is 7. + Bit 4 : NAN + + max_sta_bss: Maximum number of STA BSS (default 1, max 1) + sta_name: Name of the STA interface (default: "mlan") + max_uap_bss: Maximum number of uAP BSS (default 1, max 2) + uap_name: Name of the uAP interface (default: "uap") + max_wfd_bss: Maximum number of WIFIDIRECT BSS (default 1, max 1) + wfd_name: Name of the WIFIDIRECT interface (default: "wfd") + max_vir_bss: Number of Virtual interfaces (default 0) + nan_name: Name of the NAN interface (default: "nan") + max_nan_bss: Number of NAN interfaces (default 1) + For example, to install SD8787 driver, + insmod mlan.ko + insmod sd8787.ko [drv_mode=3] [fw_name=mrvl/sd8787_uapsta.bin] + To load driver in STA only mode, + insmod mlan.ko + insmod sd8787.ko drv_mode=1 [fw_name=mrvl/sd8787_uapsta.bin] + To load driver in uAP only mode, + insmod mlan.ko + insmod sd8787.ko drv_mode=2 [fw_name=mrvl/sd8787_uapsta.bin] + + To switch mode between STA only, uAP only and uAPSTA etc. in run time, + echo drv_mode=1 > /proc/mwlan/config // STA mode + echo drv_mode=2 > /proc/mwlan/config // uAP mode + echo drv_mode=3 > /proc/mwlan/config // STA+uAP mode + echo drv_mode=7 > /proc/mwlan/config // STA+uAP+WIFIDIRECT mode + c) Uninstall WLAN driver, + ifconfig mlanX down + ifconfig uapX down + rmmod sd8xxx + rmmod mlan + + To load driver with MFG firmware file, use mfg_mode=1 when insmod WLAN driver and + specify MFG firmware name if needed. + + There are some other parameters for debugging purpose etc. Use modinfo to check details. + drvdbg=<bit mask of driver debug message control> + dev_cap_mask=<Bit mask of the device capability> + mac_addr=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)> + auto_ds=0|1|2 <use MLAN default | enable auto deepsleep | disable auto deepsleep> + ps_mode=0|1|2 <use MLAN default | enable IEEE PS mode | disable IEEE PS mode> + p2p_enh=0|1 <Disable enhanced P2P (default) | Enable enhanced P2P> + max_tx_buf=2048|4096|8192 <maximum AMSDU Tx buffer size> + pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend> + shutdown_hs=1|0 <Enable HS when shutdown | No HS when shutdown (default)> + cfg_11d=0|1|2 <use MLAN default | enable 11d | disable 11d> + dts_enable=0|1 <Disable DTS | Enable DTS (default)> + hw_test=0|1 <Disable hardware test (default) | Enable hardware test> + fw_serial=0|1 <support parallel download FW | support serial download FW (default)> + req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API> + init_cfg=<init config (MAC addresses, registers etc.) file name> + e.g. copy init_cfg.conf to firmware directory, init_cfg=mrvl/init_cfg.conf + cal_data_cfg=<CAL data config file name> + e.g. copy cal_data.conf to firmware directory, cal_data_cfg=mrvl/cal_data.conf + txpwrlimit_cfg=<Tx power limit config file name> + e.g. copy txpwrlimit_cfg_set.conf to firmware directory, txpwrlimit_cfg=mrvl/txpwrlimit_cfg_set.conf + init_hostcmd_cfg=<init hostcmd config file name> + e.g. copy init_hostcmd_cfg.conf to firmware directory, init_hostcmd_cfg=mrvl/init_hostcmd_cfg.conf + cfg80211_wext=<bit mask of CFG80211 and WEXT control> + Bit 0: STA WEXT + Bit 1: uAP WEXT + Bit 2: STA CFG80211 + Bit 3: uAP CFG80211 + cfg80211_drcs=1|0 <Enable DRCS support (default) | Disable DRCS support> + reg_alpha2=<Regulatory alpha2 (default NULL)> + wq_sched_prio: Priority for work queue + wq_sched_policy: Scheduling policy for work queue + (0: SCHED_NORMAL, 1: SCHED_FIFO, 2: SCHED_RR, 3: SCHED_BATCH, 5: SCHED_IDLE) + Please note that, both wq_sched_prio and wq_sched_policy should be provided + as module parameters. If wq_sched_policy is (0, 3 or 5), then wq_sched_prio + must be 0. wq_sched_prio should be 1 to 99 otherwise. + rx_work=0|1|2 <default | Enable rx_work_queue | Disable rx_work_queue> + low_power_mode_enable=0|1 <disable low power mode (default)| enable low power mode> + When low power mode is enabled, the output power will be clipped at ~+10dBm and the + expected PA current is expected to be in the 80-90 mA range for b/g/n modes + indrstcfg=<2-byte IR configuration> + gpio pin (high byte): GPIO pin no to be used as trigger for out band reset + (0xFF: default pin configuration) + ir_mode (low byte) : independent reset mode + (0: disable, 1: enable out band, 2: enable in band) + For example, to enable out band reset via gpio_pin 14 + indrstcfg=0x0e01 + To enable out band reset via default gpio_pin + indrstcfg=0xff01 + To enable in band reset and disable out band reset + indrstcfg=0x02 + + Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used + for module parameters. + insmod sd8xxx.ko "<para1> <para2> ..." + +3) FOR DRIVER PROC & DEBUG + + The following info are provided in /proc/net/mwlan/mlanX/info, + on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX/info. + + driver_name = "wlan" + driver_version = <chip id, firmware version and driver version> + interface_name = "mlanX" + bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown" + media_state = "Disconnected" | "Connected" + mac_address = <6-byte adapter MAC address> + multicase_count = <multicast address count> + essid = <current SSID> + bssid = <current BSSID> + channel = <current channel> + region_code = <current region code> + multicast_address[n] = <multicast address> + num_tx_bytes = <number of bytes sent to device> + num_rx_bytes = <number of bytes received from device and sent to kernel> + num_tx_pkts = <number of packets sent to device> + num_rx_pkts = <number of packets received from device and sent to kernel> + num_tx_pkts_dropped = <number of Tx packets dropped by driver> + num_rx_pkts_dropped = <number of Rx packets dropped by driver> + num_tx_pkts_err = <number of Tx packets failed to send to device> + num_rx_pkts_err = <number of Rx packets failed to receive from device> + carrier "on" | "off" + tx queue "stopped" | "started" + + The following debug info are provided in /proc/net/mwlan/mlanX/debug, + on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX/debug. + + drvdbg = <bit mask of driver debug message control> + wmm_ac_vo = <number of packets sent to device from WMM AcVo queue> + wmm_ac_vi = <number of packets sent to device from WMM AcVi queue> + wmm_ac_be = <number of packets sent to device from WMM AcBE queue> + wmm_ac_bk = <number of packets sent to device from WMM AcBK queue> + max_tx_buf_size = <maximum Tx buffer size> + tx_buf_size = <current Tx buffer size> + curr_tx_buf_size = <current Tx buffer size in FW> + ps_mode = <0/1, CAM mode/PS mode> + ps_state = <0/1/2/3, awake state/pre-sleep state/sleep-confirm state/sleep state> + is_deep_sleep = <0/1, not deep sleep state/deep sleep state> + wakeup_dev_req = <0/1, wakeup device not required/required> + wakeup_tries = <wakeup device count, cleared when device awake> + hs_configured = <0/1, host sleep not configured/configured> + hs_activated = <0/1, extended host sleep not activated/activated> + tx_pkts_queued = <number of Tx packets queued> + pps_uapsd_mode = <0/1, PPS/UAPSD mode disabled/enabled> + sleep_pd = <sleep period in milliseconds> + qos_cfg = <WMM QoS info> + tx_lock_flag = <0/1, Tx lock flag> + port_open = <0/1, port open flag> + scan_processing = <0/1, scan processing flag> + num_tx_timeout = <number of Tx timeout> + num_cmd_timeout = <number of timeout commands> + timeout_cmd_id = <command id of the last timeout command> + timeout_cmd_act = <command action of the last timeout command> + last_cmd_id = <command id of the last several commands sent to device> + last_cmd_act = <command action of the last several commands sent to device> + last_cmd_index = <0 based last command index> + last_cmd_resp_id = <command id of the last several command responses received from device> + last_cmd_resp_index = <0 based last command response index> + last_event = <event id of the last several events received from device> + last_event_index = <0 based last event index> + num_cmd_h2c_fail = <number of commands failed to send to device> + num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device> + num_tx_h2c_fail = <number of data packets failed to send to device> + num_cmdevt_c2h_fail = <number of commands/events failed to receive from device> + num_rx_c2h_fail = <number of data packets failed to receive from device> + num_int_read_fail = <number of interrupt read failures> + last_int_status = <last interrupt status> + num_evt_deauth = <number of deauthenticated events received from device> + num_evt_disassoc = <number of disassociated events received from device> + num_evt_link_lost = <number of link lost events received from device> + num_cmd_deauth = <number of deauthenticate commands sent to device> + num_cmd_assoc_ok = <number of associate commands with success return> + num_cmd_assoc_fail = <number of associate commands with failure return> + cmd_sent = <0/1, send command resources available/sending command to device> + data_sent = <0/1, send data resources available/sending data to device> + mp_rd_bitmap = <SDIO multi-port read bitmap> + curr_rd_port = <SDIO multi-port current read port> + mp_wr_bitmap = <SDIO multi-port write bitmap> + curr_wr_port = <SDIO multi-port current write port> + cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> + event_received = <0/1, no event to process/event received and yet to process> + ioctl_pending = <number of ioctl pending> + tx_pending = <number of Tx packet pending> + rx_pending = <number of Rx packet pending> + lock_count = <number of lock used> + malloc_count = <number of malloc done> + mbufalloc_count = <number of mlan_buffer allocated> + main_state = <current state of the main process> + sdiocmd53w = <SDIO Cmd53 write status> + sdiocmd53r = <SDIO Cmd52 read status> + hs_skip_count = <number of skipped suspends> + hs_force_count = <number of forced suspends> + + Issue SDIO cmd52 read/write through proc. + Usage: + echo "sdcmd52rw=<func> <reg> [data]" > /proc/mwlan/config + where the parameters: + func: The function number to use (0-7) + reg: The address of the register + data: The value to write, read if the value is absent + For SDIO MMC driver, only function 0 and WLAN function access is allowed. + And there is a limitation for function 0 write, only vendor specific CCCR + registers (0xf0 -0xff) are permiited. + Examples: + echo "sdcmd52rw= 0 4" > /proc/mwlan/config # read func 0 address 4 + cat /proc/mwlan/config # display the register value + echo "sdcmd52rw= 1 3 0xf" > /proc/mwlan/config # write 0xf to func 1 address 3 + + Use dmesg or cat /var/log/debug to check driver debug messages. + + Update /proc/sys/kernel/printk to change message log levels. + For example, + echo 6 > /proc/sys/kernel/printk (messages with a higher priority than 6 + will be printed to the console) + echo 15 > /proc/sys/kernel/printk (all messages will be printed to console) + +4) FOR IWPRIV COMMAND + +NAME + This manual describes the usage of private commands used in Marvell MLAN + Linux Driver. + + To use parameters as hex format, a '0x' must precede it for the parameters to + be parsed properly. + +SYNOPSIS + iwpriv <mlanX> <command> [sub-command] ... + + iwpriv mlanX version + iwpriv mlanX verext + iwpriv mlanX getsignal [m] [n] + iwpriv mlanX antcfg [m] [n] + iwpriv mlanX regioncode [n] + iwpriv mlanX cfpcode [m] [n] + iwpriv mlanX wwscfg [m] + iwpriv mlanX esuppmode + iwpriv mlanX passphrase <ssid/psk/passphrase> + iwpriv mlanX httxcfg [<m>] [<n>] + iwpriv mlanX htcapinfo [<m>] [<n>] + iwpriv mlanX addbapara <m> <n> <o> <p> <q> + iwpriv mlanX aggrpriotbl <n> + iwpriv mlanX addbareject <n> + iwpriv mlanX txbufcfg + iwpriv mlanX amsduaggrctrl <n> + iwpriv mlanX httxbfcap [cap] + iwpriv mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData]" + iwpriv mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports] + iwpriv mlanX deepsleep [n] [m] + iwpriv mlanX hscfg [condition [[GPIO# [gap]]]] + iwpriv mlanX hssetpara condition [GPIO# [gap]] + iwpriv mlanX deauth [n] + iwpriv mlanX radioctrl + iwpriv mlanX reassoctrl [n] + iwpriv mlanX adhocaes + iwpriv mlanX bandcfg [l] [m] [n] [o] + iwpriv mlanX getlog + iwpriv mlanX 11dcfg + iwpriv mlanX 11dclrtbl + iwpriv mlanX wmmcfg [n] + iwpriv mlanX hotspotcfg [n] + iwpriv mlanX txpowercfg [<RateIndex> [<MinPwr> [<MaxPwr> <step>]]] + iwpriv mlanX qoscfg + iwpriv mlanX getdatarate + iwpriv mlanX txratecfg [n] + iwpriv mlanX bcninterval [n] + iwpriv mlanX sysclock [clk1] [clk2] [clk3] [clk4] + iwpriv mlanX drvdbg [n] + iwpriv mlanX mgmtframectrl + iwpriv mlanX warmreset + iwpriv mlanX regrdwr <type> <offset> [value] + iwpriv mlanX rdeeprom <offset> <length> + iwpriv mlanX memrdwr <address> [value] + iwpriv mlanX inactivityto <n> <m> <l> [k] + iwpriv mlanX sdioclock <n> + iwpriv mlanX sdcmd52rw <FN no.> <address> [data] + iwpriv mlanX scancfg [t] [m] [p] [s] [a] [b] [ext] + iwpriv mlanX sleeppd [n] + iwpriv mlanX pscfg [k] [d] [l] ... + iwpriv mlanX fwwakeupmethod [n] [g] + iwpriv mlanX getkey + iwpriv mlanX associate "<bssid> <ssid>" + iwpriv mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>] + iwpriv mlanX netmon [<act> [<filter> <band> <chan> [offset]]] + iwpriv mlanX authtype [n] + iwpriv mlanX powercons [n] + iwpriv mlanX htstreamcfg [n] + iwpriv mlanX ipaddr ["<op>;<ipaddr>"] + iwpriv mlanX macctrl [n] + iwpriv mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>] + iwpriv mlanX thermal + iwpriv mlanX indrstcfg <ir_mode> [gpio_pin] + +DESCRIPTION + Those commands are used to send additional commands to the Marvell MLAN + card via the Linux device driver. + + The mlanX parameter specifies the network device that is to be used to + perform this command on. It could be mlan0, mlan1 etc. + +version + This is used to get the current version of the driver and the firmware. + +verext + Retrieve and display an extended version string from the firmware + + Usage: + iwpriv mlanX verext [#] + + where [#] is an optional argument to retrieve a specific version string, + omission of the argument retrieves the 0 indexed string. + +getsignal + This command gets the last and average value of RSSI, SNR and NF of + Beacon and Data. + Note: This command is available only when STA is connected. + + where value of m is: + 1 -- RSSI (Receive Signal Strength Indication) + 2 -- SNR (Signal to Noise Ratio) + 3 -- NF (Noise Floor) + where value of n is: + 1 -- Beacon last + 2 -- Beacon average + 3 -- Data last + 4 -- Data average + + Examples: + iwpriv mlan0 getsignal 1 : Get the RSSI info (beacon last, beacon + average, data last and data average) + iwpriv mlan0 getsignal 3 4 : Get the NF of data average + iwpriv mlan0 getsignal 2 1 : Get the SNR of beacon last + iwpriv mlan0 getsignal : Get all of the signal info + mlan0 getsignal:-32 -33 -35 -36 67 59 63 56 -99 -92 -98 -92 + RSSI info: beacon last -32, beacon average -33, data last -35, data average -36 + SNR info: beacon last 67, beacon average 59, data last 63, data average 56 + NF info: beacon last -99, beacon average -92, data last -98, data average -92 + +antcfg + This command is used to set/get the mode of Tx/Rx path. + + where value of m is: + Bit 0 -- Tx Path A + Bit 1 -- Tx Path B + Bit 0-1 -- Tx Path A+B + + where value of n is: + Bit 0 -- Rx Path A + Bit 1 -- Rx Path B + Bit 0-1 -- Rx Path A+B + The Tx path setting (m) is used if Rx path (n) is not provided. + + Examples: + iwpriv mlan0 antcfg : Get Tx and Rx path + iwpriv mlan0 antcfg 3 : Set Tx and Rx path to A+B + iwpriv mlan0 antcfg 1 3 : Set Tx path to A and Rx path to A+B + +regioncode + This command is used to set/get the region code in the station. + Note: This command should be issued at beginning before band/channel selection + and association. + + where value is 'region code' for various regions like + USA FCC, Canada IC, Europe ETSI, Japan ... + The special code (0xff) is used for Japan to support channel 1-14 in B/G/N mode. + + Examples: + iwpriv mlan0 regioncode : Get region code + iwpriv mlan0 regioncode 0x10 : Set region code to USA (0x10) + + Note : in some case regioncode will be 0 after updated countycode or 80211d + i.e. mlanutl mlanX countrycode (CA, JP, CN, DE, ES AT, BR, RU) + or uaputl.exe sys_cfg_80211d state 1 country (CA, JP, CN, DE, ES AT, BR, RU) + Please use cfp instead of it. + +cfpcode + This command is used to set/get the Channel-Frequency-Power table codes. + The region table can be selected through region code. + The current configuration is returned if no parameter provided. + + where the parameters are, + [m]: code of the CFP table for 2.4GHz (0: unchanged) + [n]: code of the CFP table for 5GHz (0 or not provided: unchanged) + + Examples: + iwpriv mlan0 cfpcode : Get current configuration + iwpriv mlan0 cfpcode 0x30 : Set 2.4GHz CFP table code 0x30 (EU), + keep 5GHz table unchanged + iwpriv mlan0 cfpcode 0x10 5 : Set 2.4GHz CFP table code 0x10 (USA) + and 5GHz table code 5 + +wwscfg + This command is used to set/get the WWS (World Wide Safe) mode. + + where value of m is: + 0 -- Disable WWS mode (default) + 1 -- Enable WWS mode + + Examples: + iwpriv mlan0 wwscfg : Get WWS mode + iwpriv mlan0 wwscfg 1 : Enable WWS mode + iwpriv mlan0 wwscfg 0 : Disable WWS mode + +esuppmode + This command is used to get the current RSN mode and active pairwise/group + cipher for WPA/WPA2 mode. + Note: This command is available only when STA is connected. + + These are bits settings used to indicate each RSN mode. + Bit 0 : No RSN + Bit 1-2 : RFU + Bit 3 : WPA + Bit 4 : WPA-NONE + Bit 5 : WPA2 + Bit 6 : AES + Bit 7-15 : RFU + + These are bits settings used to indicate each pairwise and group cipher. + Bit 0 : RFU + Bit 1 : RFU + Bit 2 : TKIP + Bit 3 : AES + Bit 2-7 : RFU + + Example: + iwpriv mlan0 esuppmode : Get RSN mode and pairwise/group cipher + 8 4 4 + (The current RSN mode is WPA, active pairwise cipher is TKIP and + active group cipher is TKIP.) + +passphrase + This command is used to set/get passphrase for WPA-PSK/WPA2-PSK mode. + + Where <n> + ASCII string for ssid/passphrase/psk. + + 1) "0;<ssid=valid ssid>" - This will get the passphrase, AKMP + for specified ssid, if none specified then it will get all. + + Example: + iwpriv mlan0 passphrase "0;ssid=marvell" + + 2) "1;<psk=64 byte hexpsk>;<passphrase=1-63 byte passphare> + <ssid=valid ssid>" - Passphrase and psk cannot be provided for the same SSID. + This command takes only one SSID at a time, If ssid= is present it should contain + a passphrase or psk. If no arguments are provided then AKMP=802.1x, and passphrase + should be provided after association. + End of each parameter should be followed by a ';'(except for the last parameter) + as the delimiter. If ';' or '/' has to be used in an SSID then a '/' should be preceded + to ';' or '/' as a escape. + + Examples: + iwpriv mlan0 passphrase "1;ssid=mrvlAP;passphrase=abcdefgd" + iwpriv mlan0 passphrase "1;ssid=mrvl AP;psk=<64 bytes hexpsk>" + + If user wants to input the ssid as "mrvl; AP" then command has to be + iwpriv mlan0 passphrase "1;ssid=mrvl/; AP;passphrase=abcdefgh" + + If user wants to input the ssid as "//;" then command has to be + iwpriv mlan0 passphrase "1;ssid=/////;;passphrase=abcdefgh" + + 3) "2;<ssid=valid ssid>" - This will clear the passphrase + for specified ssid, if none specified then it will clear all. + + Examples: + iwpriv mlan0 passphrase "2;ssid=marvell" + iwpriv mlan0 passphrase "2" : Clear all profiles and disable embedded supplicant + +httxcfg + This command is used to configure various 11n specific configuration + for transmit (such as Short GI, Channel BW and Green field support) + + where <m> is <txcfg> + This is a bitmap and should be used as following + Bit 15-8: Reserved set to 0 + Bit 7: STBC enable/disable + Bit 6: Short GI in 40 Mhz enable/disable + Bit 5: Short GI in 20 Mhz enable/disable + Bit 4: Green field enable/disable + Bit 3-2: Reserved set to 1 + Bit 1: 20/40 Mhz enable disable. + Bit 0: LDPC enable/disable + + When Bit 1 is set then firmware could transmit in 20Mhz or 40Mhz based + on rate adaptation. When this bit is reset then firmware will only + transmit in 20Mhz. + + where <n> is <band> + <band> - This is the band info for <txcfg> settings. + 0: Settings for both 2.4G and 5G bands + 1: Settings for 2.4G band + 2: Settings for 5G band + + Examples: + iwpriv mlanX httxcfg + This will display HT Tx configuration. + If the configurations for 2.4G and 5G are different, + the first value is for 2.4G and the second value is for 5G. + Otherwise, it will display a single value for both bands. + + iwpriv mlanX httxcfg 0x62 + This will enable 20/40 and Short GI but will disable Green field for 2.4G and 5G band. + + iwpriv mlanX httxcfg 0x30 1 + This will enable Short GI 20 Mhz and Green field for 2.4G band. + + The default value is 0x20 for 2.4G and 0x62 for 5G. + + Note:- If 20/40 MHz support is disabled in htcapinfo, device will not transmit + in 40 MHz even 20/40 MHz is enabled in httxcfg. + +htcapinfo + This command is used to configure some of parameters in HTCapInfo IE + (such as Short GI, Channel BW, and Green field support) + + where <m> is <capinfo> + <capinfo> - This is a bitmap and should be used as following + Bit 29: Green field enable/disable + Bit 26: Rx STBC Support enable/disable. (As we support + single spatial stream only 1 bit is used for Rx STBC) + Bit 24: Short GI in 40 Mhz enable/disable + Bit 23: Short GI in 20 Mhz enable/disable + Bit 17: 20/40 Mhz enable disable. + Bit 8: Enable/disable 40Mhz Intolarent bit in ht capinfo. + 0 will reset this bit and 1 will set this bit in + htcapinfo attached in assoc request. + All others are reserved and should be set to 0. + + Setting of any other bits will return error. + + where <n> is <band> + <band> - This is the band info for <capinfo> settings. + 0: Settings for both 2.4G and 5G bands + 1: Settings for 2.4G band + 2: Settings for 5G band + + Examples: + iwpriv mlanX htcapinfo + This will display HT capabilties information. + If the information for 2.4G and 5G is different, + the first value is for 2.4G and the second value is for 5G. + Otherwise, it will display a single value for both bands. + + iwpriv mlanX htcapinfo 0x1820000 + This will enable Short GI, Channel BW to 20/40 and disable Green field support for 2.4G and 5G band. + + iwpriv mlanX htcapinfo 0x800000 2 + This will enable Short GI, Channel BW to 20 only, No Rx STBC support and disable Green field support for 5G band. + + The default value is 0x4800000 for 2.4G and 0x5820000 for 5G. + + Note:- This command can be issued any time but it will only come to effect from + next association. (as HTCapInfo is sent only during Association). + +addbapara + This command can be used to update the default ADDBA parameters. + + where <m> is <timeout> + <timeout> - This is the block ack timeout for ADDBA request. + 0 : Disable (recommended for throughput test) + 1 - 65535 : Block Ack Timeout in TU + + where <n> is <txwinsize> + <txwinsize> - Window size for ADDBA request. (16 is recommended and default value) + + where <o> is <rxwinsize> + <rxwinsize> - Window size for ADDBA response. (48 is recommended and 32 is default value) + (16 is recommended for IWNCOMM AP in WAPI throughput test) + + Current window size limit for Tx as well as Rx is 1023. + + where <p> is <txamsdu> + <txamsdu> - amsdu support for ADDBA request. (1 is default value) + 0: disable amsdu in ADDBA request + 1: enable amsdu in ADDBA request + + where <q> is <rxamsdu> + <rxamsdu> - amsdu support for ADDBA response. (1 is default value) + 0: disable amsdu in ADDBA response + 1: enable amsdu in ADDBA response + + eg: + iwpriv mlanX addbapara - This command will get the current addba params + iwpriv mlanX addbapara 1000 64 8 0 0 - This will change the ADDBA timeout to (1000 * 1024) us, + txwinsize to 64 and rxwinsize to 8 and disable amdsu in ADDBA request/response. + + The default setting is 65535 16 32 1 1. + + In case the ADDBA timeout value is updated then a ADDBA is sent for all streams + to update the timeout value. + + In case txwinsize and/or rxwinsize is updated, the effect could only be seen on + next ADDBA request/response. The current streams will not be affected with this + change. + + In case of txamsdu/rxamsdu is updated, the effect could only be seen on + next ADDBA request/response. The current streams will not be affected with this + change. AMSDU in AMPDU stream will be enabled when AP support this feature + and AMSDU is enabled in aggrpriotbl. + +aggrpriotbl + This command is used set/get the priority table for AMPDU/AMSDU traffic per tid. + This command can also be used to disable AMPDU/AMSDU for a given tid. + In case of AMPDU this priority table will be used to setup block ack (to make + sure the highest priority tid always uses AMPDU as we have limited AMPDU streams) + + where <m0> <n0> <m1> <n1> ... <m7> <n7> + + <mx> - This is priority for Tid0 for AMPDU packet. A priority could be any + values between 0 - 7, 0xff to disable aggregation. + <nx> - This is priority for Tid0 for AMSDU packet. A priority could be any + values between 0 - 7, 0xff to disable aggregation. + + eg: + iwpriv mlanX aggrpriotbl - This command will get the current Priority table for AMPDU and AMSDU. + <2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255>. This is read as + <"Prio for AMPDU for Tid0" "Prio for AMSDU for Tid0" + "Prio for AMPDU for Tid1" "Prio for AMSDU for Tid1" and so on + iwpriv mlanX aggrpriotbl 2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255 - + This will set the priority table for AMPDU and AMSDU + Priority for Tid0/AMPDU = 2, Tid0/AMSDU = 2, Tid1/AMPDU = 0, Tid1/AMSDU = 0 + and so on. Aggregation for Tid6 and Tid7 are disabled. + Here higher the priority number, higher the priority (i.e. 7 + has higher priority than 6). Similarly for AMSDU. + iwpriv mlanX aggrpriotbl 0xff 2 0xff 0 0xff 1 0xff 3 0xff 4 0xff 5 0xff 0xff 0xff 0xff - This will disable + AMPDU for all the TIDs but will still keep AMSDU enabled to Tid0 to Tid5 + + The default setting is 2 255 0 255 1 255 3 255 4 255 5 255 255 255 255 255. + + A delBA should be seen in case a disable happens on a TID for which AMPDU stream + is currently setup. + + Note:- This command should only be issue in disconnected state. + +addbareject + This command is used set/get the addbareject table for all the TIDs. + This command can also be used to enable rejection of ADDBA requests for a given tid. + + where <m0> <m1> ... <m7> + + <mX> - This can be 0/1 for TidX. 1 enables rejection of ADDBA request for TidX and + 0 would accept any ADDBAs for TidX. + + eg: + iwpriv mlanX addbareject - This command will get the current table. + [0 0 0 0 0 0 0 0]. ADDBA would be accepted for all TIDs. This is the default state. + + iwpriv mlanX addbareject 0 0 1 1 0 0 0 0 - This command will accept ADDBA requests for + Tid [0,1,4,5,6,7] and reject ADDBA requests for Tid [2,3] + + iwpriv mlanX addbareject 1 1 1 1 1 1 1 1 - This will enable rejection of ADDBA requests for + all Tids. + + Note:- This command should only be issue in disconnected state. + +txbufcfg + This command can be used to get current buffer size. + + eg: + iwpriv mlanX txbufcfg - This will display the current buffer size. + + Note:- The actual tx buf size will depends on AP's capability and max transmit buffer size. + +amsduaggrctrl + This command could be used to enable/disable a feature where firmware gives feedback to driver + regarding the optimal AMSDU buffer size to use with the current rate. Firmware will use the + current rate to decide the buffer size we could transmit. The max buffer size will still be + limited by buffer size provided in txbufcfg. (i.e. if the txbufcfg is 4K, then we could only transmit + 4K/2K AMSDU packets, if the txbufcfg is 8K then we could transmit 8k/4k/2k based on current rate) + + If enabled AMSDU buffer size at various rates will be as follows + + 1. Legacy B/G rate. + No AMSDU aggregation. + + 2. BW20 HT Rate: + When TX rate goes down, + MCS 7, 6, 5, 4: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 3, 2: + a 4K aggregation size (if TX buffer size is 8K/4K) + b 2K aggregation size (if TX buffer size is 2K) + + MCS 1, 0: + a No aggregation + + When TX rate goes up, + MCS 7, 6, 5: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 4, 3: + a 4K aggregation size (if TX buffer size is 8K/4K) + b 2K aggregation size (if TX buffer size is 2K) + + MCS 2, 1, 0: + a No aggregation + + 3. BW40 HT Rate: + When TX rate goes down, + MCS 7, 6, 5, 4, 3, 2, 1: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 0: + a No aggregation + + When TX rate goes up, + MCS 7, 6, 5, 4, 3: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 2, 1, 0: + a No aggregation + + where <n> is 0/1 (for disable/enable) + + eg: + iwpriv mlanx amsduaggrctrl 1 - Enable this feature + iwpriv mlanx amsduaggrctrl 0 - Disable this feature + iwpriv mlanx amsduaggrctrl - This will get the enable/disable flag + and the current AMSDU buffer size). The AMSDU buffer size returned is only + valid after association as before association there is no rate info. + + Note:- This command to enable/disable could be given anytime (before/after + association). This feature is enabled by default by the driver during + initialization. + +httxbfcap + This command is used to set/get the TX beamforming capabilities. + + Usage: + iwpriv mlanX httxbfcap [cap] + + where the parameters are, + cap: TX beamforming capabilities + Bit 0 : Implicit TX BF receiving capable + Bit 1 : RX staggered sounding capable + Bit 2 : TX staggered sounding capable + Bit 3 : RX NDP capable + Bit 4 : TX NDP capable + Bit 5 : Implicit TX BF capable + Bit 6-7 : Calibration + 0: - not supported + 1: - STA can respond to a calibration request using + the CSI Report, but cannot initiate calibration + 2: - reserved + 3: - STA can both initiate and respond to a calibration request + Bit 8 : Explicit CSI TX BF capable + Bit 9 : Explicit non-compressed steering capable + Bit 10 : Explicit compressed steering capable + Bit 11-12: Explicit TX BF CSI feedback + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 13-14: Explicit non-compressed BF feedback capable + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 15-16: Explicit compressed BF feedback capable + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 17-18: Minimal grouping + 0: - no grouping (STA supports groups of 1) + 1: - groups of 1, 2 + 2: - groups of 1, 4 + 3: - groups of 1, 2, 4 + Bit 19-20: CSI number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 21-22: Non-compressed steering number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 23-24: Compressed steering number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 25-26: CSI max number of rows beamformer supported + 0: - single row of CSI + 1: - 2 rows of CSI + 2: - 3 rows of CSI + 3: - 4 rows of CSI + Bit 27-28: Channel estimation capability + 0: - 1 space time stream + 1: - 2 space time streams + 2: - 3 space time streams + 3: - 4 space time streams + Bit 29-31: Reserved + + Examples: + iwpriv mlan0 httxbfcap : Get the current TX BF capabilities + iwpriv mlan0 httxbfcap 0x0000001F : Set the TX BF capabilities of the + Implicit TX BF receiving capable, + RX staggered sounding capable, + TX staggered sounding capable, + RX NDP capable and TX NDP capable + +httxbfcfg + This command is used to configure the TX beamforming options. + Note: Any new subcommand should be inserted in the second + argument and each argument of the sub command should be + separated by semicolon. For global configuration, the + arguments should be separated by space. + + Usage: + iwpriv mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData]" + + where the parameters are, + action: TX beamforming action + 0: Control global parameters for beamforming + 1: Performs NDP Sounding for PEER + 2: TX BF interval in milliseconds + 3: Enable/Disable beamforming/sounding for a particular peer + 4: TX BF SNR Threshold for peer + .. <for new subcommand> + GlobalData: Global parameter arguments. + It contains beamforming enable, sounding enable, FB type, snr_threshold + sounding interval, Beamformig mode values seperated by space. + Syntax: + iwpriv mlanX httxbfcfg <action>;<beamforming enable> <sounding enable> <FB type> + <snr_threshold> <sounding interval> <Beamforming mode> + tsData: Trigger sounding for PEER specific arguments, + it contains PEER MAC and status + interval: TX BF interval in milliseconds + txPeerData: Enable/Disable beamforming/sounding for the indicated peer, + it contains PEER MAC, sounding, beamfoming options and FB type; + snrData: TX BF SNR Threshold for peer, it contains PEER MAC and SNR + + Examples: + iwpriv mlan0 httxbfcfg "0" : Get current global configuration parameter + iwpriv mlan0 httxbfcfg "2;00:50:43:20:BF:64" : Get the TX BF periodicity for a given peer + iwpriv mlan0 httxbfcfg "3" : Get the list of MAC addresses that have + beamforming and/or sounding enabled + iwpriv mlan0 httxbfcfg "4" : Get the list of PEER MAC, SNR tuples + programmed into the firmware. + iwpriv mlan0 httxbfcfg "0;0 0 3 10 500 5" : Disable beamforming, sounding, set FB type + to 3, snr threshold to 10, sounding interval + to 500 ms and beamforming mode to 5 + iwpriv mlan0 httxbfcfg "1;00:50:43:20:BF:64" : Perform NDP Trigger sounding to peer + 00:50:43:20:BF:64 + iwpriv mlan0 httxbfcfg "2;00:50:43:20:BF:64;500" : Set TX BF periodicity for peer 00:50:43:20:BF:64 + to 500 milliseconds + iwpriv mlan0 httxbfcfg "3;00:50:43:20:BF:43;1;0;3" : Enable beamforming, disable sounding and set + FB type to 3 for peer 00:50:43:20:BF:43 + iwpriv mlan0 httxbfcfg "4;00:50:43:20:BF:24;43" : Set TX BF SNR threshold to peer + 00:50:43:20:BF:24 with SNR 43 + +mgmtframectrl + This command is used to get/set mask for the management frames which needs to be forwarded to application layer. + + Usage: + iwpriv mlanX mgmtframectrl [m] + + where the parameter [m] is the bit mask of management frame reception. + Following are the bit definitions. + Bit 0 : Association Request + Bit 1 : Association Response + Bit 2 : Re-Association Request + Bit 3 : Re-Association Response + Bit 4 : Probe Request + Bit 5 : Probe Response + Bit 8 : Beacon Frames + + Examples: + iwpriv mlan0 mgmtframectrl : Get the current Mgmt Frame forwarding mask + iwpriv mlan0 mgmtframectrl 0x0020 : Bit 5 is set, Forward probe response + frames to application layer + +mpactrl + This command is used to set/get the Tx, Rx SDIO aggregation parameters. + Note: The parameters can be set only in disconnected state. + + Usage: + iwpriv mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports] + + where the parameter are: + [tx_ena]: Enable/disable (1/0) Tx MP-A + [rx_ena]: Enable/disable (1/0) Rx MP-A + [tx_size]: Size of Tx MP-A buffer + [rx_size]: Size of Rx MP-A buffer + [tx_ports]: Max ports (1-16) for Tx MP-A + [rx_ports]: Max ports (1-16) for Rx MP-A + default values are 1 1 16384 32768 16 16 + The MP-A may be disabled by default at build time if the MMC driver byte mode patch + is not available in kernel. + + Examples: + iwpriv mlan0 mpactrl : Get MP aggregation parameters + iwpriv mlan0 mpactrl 0 0 + : Disable MP aggregation for Tx, Rx respectively + iwpriv mlan0 mpactrl 1 1 8192 8192 8 8 + : Enable MP aggregation for Tx, Rx + : Set Tx, Rx buffer size to 8192 bytes + : Set maximum Tx, Rx ports to 8 + +deepsleep + This command is used to set/get auto deep sleep mode. + + Usage: + iwpriv mlanX deepsleep [n] [m] + + where the parameters are: + [n]: Enable/disable auto deep sleep mode (1/0) + [m]: Idle time in milliseconds after which firmware will put the device + in deep sleep mode. Default value is 100 ms. + + Examples: + iwpriv mlan0 deepsleep : Display auto deep sleep mode + iwpriv mlan0 deepsleep 1 : Enable auto deep sleep mode, idle time unchanged + iwpriv mlan0 deepsleep 0 : Disable auto deep sleep mode + iwpriv mlan0 deepsleep 1 500 : Enable auto deep sleep mode with idle time 500 ms + Note: + Deepsleep must be disabled before changing idle time. + +hscfg + This command is used to configure the host sleep parameters. + + Usage: + iwpriv mlanX hscfg [condition [[GPIO# [gap]]]] + + This command takes one (condition), two (condition and GPIO#) or three + (condition, GPIO# and gap) parameters for set. If no parameter provided, + get is performed. + + where Condition is: + bit 0 = 1 -- broadcast data + bit 1 = 1 -- unicast data + bit 2 = 1 -- mac event + bit 3 = 1 -- multicast data + bit 6 = 1 -- Wakeup when mgmt frame received. + bit 7 = 1 -- Reserved + bit 8 = 1 -- Disable non maskable data wakeup. + + The host sleep mode will be canceled if condition is set to -1. The default is 0x7. + + where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid + GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO will be used instead). + The default is 0xff. + + where Gap is the gap in milliseconds between wakeup signal and wakeup event or 0xff + for special setting (host acknowledge required) when GPIO is used to wakeup host. + The default is 200. + + The host sleep set except for cancellation will be blocked if host sleep is + already activated. + + Examples: + iwpriv mlan0 hscfg : Get current host sleep mode + iwpriv mlan0 hscfg -1 : Cancel host sleep mode + iwpriv mlan0 hscfg 3 : Broadcast and unicast data + Use GPIO and gap set previously + iwpriv mlan0 hscfg 2 3 : Unicast data + Use GPIO 3 and gap set previously + iwpriv mlan0 hscfg 2 1 0xa0 : Unicast data + Use GPIO 1 and gap 160 ms + iwpriv mlan0 hscfg 2 0xff : Unicast data + Use interface (e.g. SDIO) + Use gap set previously + iwpriv mlan0 hscfg 4 3 0xff : MAC event + Use GPIO 3 + Special host sleep mode + iwpriv mlan0 hscfg 1 0xff 0xff : Broadcast data + Use interface (e.g. SDIO) + Use gap 255ms + +hssetpara + This command is used to set host sleep parameters. + + Usage: + iwpriv mlanX hssetpara Condition [GPIO# [gap]] + + Note: + 1) The usages of parameters are the same as "hscfg" command. + 2) The parameters will be saved in the driver and be used when host suspends. + +deauth + This command is used to send a de-authentication to an arbitrary AP. + If [n] is omitted, the driver will deauth the associated AP. + If in ad-hoc mode this command is used to stop beacon transmission + from the station and go into idle state. + + When <n> is supplied as a MAC address, the driver will deauth the + specified AP. If the AP address matches the driver's associated AP, + the driver will disconnect. Otherwise, the driver remains connected. + +radioctrl + This command is used to turn on/off the radio. + Note: The radio can be disabled only in disconnected state. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + iwpriv mlan0 radioctrl 1 : Turn the radio on + iwpriv mlan0 radioctrl : Get radio status + +reassoctrl + This command is used to turn on/off re-association in driver. + + Usage: + iwpriv mlanX reassoctrl [n] + + Where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + iwpriv mlan0 reassoctrl : Get re-association status + iwpriv mlan0 reassoctrl 1 : Turn re-association on + +adhocaes + This command is used to set/get the AES key, when the station is in ad-hoc mode. + Note: This command is only available in disconnected state. + + where value can be any 16 byte value. + + Examples: + iwpriv mlan0 adhocaes : Get ad-hoc aes key + iwpriv mlan0 adhocaes "1;12345678901234567890123456789012" + : Set ad-hoc aes key + iwpriv mlan0 adhocaes 2 : Clear ad-hoc aes key + +bandcfg + This command is used to set/get infra/ad-hoc band. + Note: This command is only available in disconnected state. + + Usage: + iwpriv mlanX bandcfg [l] [m] [n] [o] + + where the parameters: + [l]: Infrastructure band + bit 0: B + bit 1: G + bit 2: A + bit 3: GN + bit 4: AN + + bit 5: AC 2.4G + bit 6: AC 5G + [m]: Ad-hoc start band + bit 0: B + bit 1: G + bit 2: A + bit 3: GN + bit 4: AN + bit 5: AC 2.4G + bit 6: AC 5G + [n]: Ad-hoc start channel + [o]: 0 - Bandwidth 20Mhz + 1 - HT Bandwidth 40Mhz above + 3 - HT Bandwidth 40Mhz below + 4 - VHT Bandwidth 80Mhz + Examples: + iwpriv mlan0 bandcfg : Get infra/ad-hoc band and ad-hoc + start channel configurations + iwpriv mlan0 bandcfg 1 : Set infra band to B only + iwpriv mlan0 bandcfg 3 2 6 : Set infra band to B/G, ad-hoc start band + to G and ad-hoc start channel to 6 + iwpriv mlan0 bandcfg 7 11 6 1 : Set infra band to B/G/A, ad-hoc start band + to B/G/GN, ad-hoc start channel to 6 and + secondary channel to above + +getlog + This command is used to get the statistics available in the station. + +11dcfg + This command is used to control 11D. No argument is used to get. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + iwpriv mlan0 11dcfg 1 : Enable 11D + iwpriv mlan0 11dcfg : Get 11D status + +11dclrtbl + This command is used to clear the 11D channel table. + + Usage: + iwpriv mlanX 11dclrtbl + +wmmcfg + This command is used to control WMM. No argument is used to get. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + iwpriv mlan0 wmmcfg 1 : Enable WMM + iwpriv mlan0 wmmcfg : Get WMM status + +hotspotcfg + This command is used to get/set the HotSpot configuration. + + WHere + <n> - Configuration bitset + This is a bitmap and should be used as following + Bit 31-10: Reserved set to 0 + Bit 9: TDLS support indication enable/disable + Bit 8: Interworking indication enable/disable + Bit 7-1: Reserved set to 0 + Bit 0: HotSpot feature enable/disable + +txpowercfg + This command is used to get/set the Tx power configuration. + + Where + <RateIndex> - Data rate index + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 6 Mbps + 5 9 Mbps + 6 12 Mbps + 7 18 Mbps + 8 24 Mbps + 9 36 Mbps + 10 48 Mbps + 11 54 Mbps + 12 MCS0 (BW20) + 13 MCS1 (BW20) + 14 MCS2 (BW20) + 15 MCS3 (BW20) + 16 MCS4 (BW20) + 17 MCS5 (BW20) + 18 MCS6 (BW20) + 19 MCS7 (BW20) + 20 MCS8 (BW20) + 21 MCS9 (BW20) + 22 MCS10 (BW20) + 23 MCS11 (BW20) + 24 MCS12 (BW20) + 25 MCS13 (BW20) + 26 MCS14 (BW20) + 27 MCS15 (BW20) + 140 MCS0 (BW40) + 141 MCS1 (BW40) + 142 MCS2 (BW40) + 143 MCS3 (BW40) + 144 MCS4 (BW40) + 145 MCS5 (BW40) + 146 MCS6 (BW40) + 147 MCS7 (BW40) + 148 MCS0 (BW40) + 149 MCS1 (BW40) + 150 MCS2 (BW40) + 151 MCS3 (BW40) + 152 MCS4 (BW40) + 153 MCS5 (BW40) + 154 MCS6 (BW40) + 155 MCS7 (BW40) + 0xff Default + <MinPwr> - Minimum power level in dBm + <MaxPwr> - Maximum power level in dBm + <step> - Power step + + Note: Firmware may adjust the setting if over limit, final value can be + verified using get command. + + Examples: + iwpriv mlan0 txpowercfg 0xff : Default power configuration + iwpriv mlan0 txpowercfg 11 12 : Set power level 12 dBm to data rate 54 Mbps + iwpriv mlan0 txpowercfg 7 11 16 1 : Set power level 11 dBm to 16 dBm with + step 1 to data rate 18 Mbps + iwpriv mlan0 txpowercfg : Get current configuration + mlan0 txpowercfg:2 3 13 18 2 1 1 13 18 2 0 0 13 18 2 + 10 11 13 15 2 8 9 13 16 2 6 7 13 17 2 4 5 13 17 2 + 17 19 13 15 2 15 16 13 16 2 13 14 13 17 2 12 12 13 17 2 + 145 147 13 14 1 143 144 13 14 1 141 142 13 14 1 140 140 13 14 1 + + 2 -> First rate index is 5.5 Mbps. + 3 -> Last rate index is 11 Mbps. + 13 -> Min Tx power value is 13 dBm. + 18 -> Max Tx power value is 18 dBm. + 2 -> Power adjustment step value is 2. + + Similarly + 17 -> First rate index is MCS5 (BW20). + 19 -> Last rate index is MCS7 (BW20). + 13 -> Min Tx power value is 13 dBm. + 15 -> Max Tx power value is 15 dBm. + 2 -> Power adjustment step value is 2. + + so on... + +qoscfg + This command sets WMM IE QOS info when an argument is given, and gets current WMM + IE QOS info when no argument is given. + + Examples: + iwpriv mlan0 qoscfg 0x0f : Set WMM IE QOS info to 0x0f + iwpriv mlan0 qoscfg : Get WMM IE QOS info + +getdatarate + This command is used to get the data rate (index) being used in last Tx + packet and last Rx packet. + +bcninterval + This command is used to set/get the beacon interval in ad-hoc mode. + The valid beacon interval is between 20 - 1000, default beacon + interval is 100. + + Where <n> + Beacon interval in TU (Time Unit: 1024 us). + + Examples: + iwpriv mlan0 bcninterval 200 : Set ad-hoc beacon interval to 200 + iwpriv mlan0 bcninterval : Get ad-hoc beacon interval + +sysclock + This command is used to set/get system clocks in MHz. + The current system clock, configurable system clocks and all of the + supported system clocks will be returned if no parameter provided. + + Examples: + iwpriv mlan0 sysclock : Get system clocks + 80 80 128 128 128 5 11 16 20 22 32 40 44 64 80 106 128 160 ... + (The current system clock is 80 MHz. + The configurable system clocks of non-security, security, non-security + A-MPDU and security A-MPDU are 80 MHz, 128 MHz, 128 MHz and 128 MHz. + The supported system clocks are 5 MHz, 11 MHz, ..., 160 MHz, 182 MHz, + 213 MHz, 256 MHz, 320 Mhz, 366 MHz , ... . the Max system clocks is different + for different chips, you could use this command to get the supported system clock) + + iwpriv mlanX sysclock 80 : Set system clock in non-security mode + to 80 MHz, no change for others + iwpriv mlanX sysclock 0 0 128 : Set system clock in non-security A-MPDU + mode to 128 MHz, no changes for others + +drvdbg + This command is used to set/get the bit masks of driver debug message control. + + Usage: + iwpriv mlanX drvdbg [n] + + Where the parameter <n> is the generic debug message control bit mask. + The following types of driver debug messages can be dynamically enabled or + disabled by setting or clearing the corresponding bits, + bit 0: MMSG PRINTM(MMSG,...) + bit 1: MFATAL PRINTM(MFATAL,...) + bit 2: MERROR PRINTM(MERROR,...) + bit 3: MDATA PRINTM(MDATA,...) + bit 4: MCMND PRINTM(MCMND,...) + bit 5: MEVENT PRINTM(MEVENT,...) + bit 6: MINTR PRINTM(MINTR,...) + bit 7: MIOCTL PRINTM(MIOCTL,...) + ... + bit 16: MDAT_D PRINTM(MDAT_D,...), DBG_HEXDUMP(MDAT_D,...) + bit 17: MCMD_D PRINTM(MCMD_D,...), DBG_HEXDUMP(MCMD_D,...) + bit 18: MEVT_D PRINTM(MEVT_D,...), DBG_HEXDUMP(MEVT_D,...) + bit 19: MFW_D PRINTM(MFW_D,...), DBG_HEXDUMP(MFW_D,...) + bit 20: MIF_D PRINTM(MIF_D,...), DBG_HEXDUMP(MIF_D,...) + ... + bit 28: MENTRY PRINTM(MENTRY,...), ENTER(), LEAVE() + bit 29: MWARN PRINTM(MWARN,...) + bit 30: MINFO PRINTM(MINFO,...) + + If CONFIG_DEBUG=2, all kinds of debug messages can be configured. + + If CONFIG_DEBUG=1, all kinds of debug messages can be configured except + for MENTRY, MWARN and MINFO. By default MMSG, MFATAL and MERROR are enabled. + + Some special debug messages, + '*' // MLAN driver ISR is called (bit 6 MINTR enabled) + '|' // PS awake event is received (bit 5 MEVENT enabled) + '_' // PS sleep event is received (bit 5 MEVENT enabled) + '+' // PS sleep confirm is sent (bit 5 MEVENT enabled) + + Examples: + iwpriv mlan0 drvdbg : Get the current driver debug masks + iwpriv mlan0 drvdbg 0 : Disable all the debug messages + iwpriv mlan0 drvdbg 7 : Enable MMSG, MFATAL and MERROR messages + iwpriv mlan0 drvdbg 0x20037 : Enable MMSG, MFATAL, MEEROR, + MCMND, MEVENT and MCMD_D messages + iwpriv mlan0 drvdbg -1 : Enable all the debug messages + +warmreset + This command is used for warm reset of the interface. + + Usage: + iwpriv mlanX warmreset + +regrdwr + This command is used to read/write the adapter register. + + Usage: + iwpriv mlanX regrdwr <type> <offset> [value] + + where the parameters are, + <type>: 1:MAC/SOC, 2:BBP, 3:RF, 5:CAU + <offset>: offset of register + [value]: value to be written + Note: If highest bit of a 32-bit value needs to be set, use negative input. + The calculation is -(0xffffffff - value + 1). Range from -0x80000000 to -1. + + Examples: + iwpriv mlan0 regrdwr 1 0xa060 : Read the MAC register + iwpriv mlan0 regrdwr 1 0xa060 0x12 : Write the MAC register + iwpriv mlan0 regrdwr 1 0xa794 -0x80000000 + : Write 0x80000000 to MAC register + iwpriv mlan0 regrdwr 1 0xa794 -0x00000001 + : Write 0xffffffff to MAC register + +rdeeprom + This command is used to read the EEPROM contents of the card. + + Usage: + iwpriv mlanX rdeeprom <offset> <length> + + where the parameters are, + <offset>: multiples of 4 + <length>: 4-20, multiples of 4 + + Example: + iwpriv mlan0 rdeeprom 0 20 : Read 20 bytes of EEPROM data from offset 0 + +memrdwr + This command is used to read/write the adapter memory. + + Usage: + iwpriv mlanX memrdwr <address> [value] + + where the parameters are, + <address>: memory address + [value]: value to be written + Note: If highest bit of a 32-bit address/value needs to be set, use negitive input. + The calculation is -(0xffffffff - address/value + 1). Range from -0x80000000 to -1. + + Examples: + iwpriv mlan0 memrdwr 0x4cf70 : Read memory address 0x4cf70 + iwpriv mlan0 memrdwr -0x80000000 + : Read memory address 0x80000000 + iwpriv mlan0 memrdwr -0x7fff6000 -0x40000000 + : Write 0xc0000000 to memory address 0x8000a000 + +inactivityto + This command is used to set/get the inactivity timeout value, which specifies + when WLAN device is put to sleep. + + Usage: + iwpriv mlanX inactivityto <n> <m> <l> [k] + + where the parameter are: + <n>: timeout unit in microseconds. + <m>: Inactivity timeout for unicast data. + <l>: Inactivity timeout for multicast data. + [k]: Inactivity timeout for new Rx traffic after PS notification to AP. + + Examples: + iwpriv mlan0 inactivityto : Get the timeout value + iwpriv mlan0 inactivityto 1000 2 3 : Set timeout unit to 1000 us (1 ms), + inactivity timeout for unicast data is 2 ms, + inactivity timeout for multicast data is 3 ms + +sdioclock + Turn On(1) or Off(0) the SDIO clock. + + Usage: + iwpriv mlanX sdioclock 1 (on) + iwpriv mlanX sdioclock 0 (off) + iwpriv mlanX sdioclock (get the current clock state) + +sdcmd52rw + This command is used to read/write a controller register in + Secure Digital I/O Interfaces. + + Usage: + iwpriv mlanX sdcmd52rw <function number> <register address> [value] + + For SDIO MMC driver, only function 0 and 1 access is allowed. And there + is a limitation for function 0 write, only vendor specific CCCR registers + (0xf0 -0xff) are permiited. + + Examples: + iwpriv mlan0 sdcmd52rw 1 3 + iwpriv mlan0 sdcmd52rw 1 1 0x3f + +scancfg + This command is used to set/get scan configuration parameters. + + Usage: + iwpriv mlanX scancfg [t] [m] [p] [s] [a] [b] [ext] + + where the parameters: + [t]: Scan Type (0: Unchanged, 1: Active, 2: Passive, default Active) + [m]: Scan Mode (0: Unchanged, 1: BSS, 2: IBSS, 3: Any, default Any) + [p]: Scan Probes (0: Unchanged, 1-4: Number of probes per channel, default 4) + [s]: Specific Scan Time (0: Unchanged, n: Value in ms, default 110 ms, max 500 ms) + [a]: Active Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 500 ms) + [b]: Passive Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 2000 ms) + [ext]: Extended scan (0: Legacy scan, 1: Extended scan) + + No change if the parameter is 0 or the parameter is not provided. + + Examples: + iwpriv mlan0 scancfg : Get all the current scan configuration settings + iwpriv mlan0 scancfg 1 3 : Set scan type to active and scan mode to any, + all the other scan configurations are unchanged + iwpriv mlan0 scancfg 0 1 2 200 : Set scan mode to BSS, number of probes to 2 and + specific scan time to 200 ms, all the other scan + configurations are unchanged + +sleeppd + This command is used to configure the sleep period of the WLAN device. + + Usage: + iwpriv mlanX sleeppd [<period>] + + Where the parameter is: + period: sleep period in milliseconds. Range 10~60. 0 for disable. + + Examples: + iwpriv mlan0 sleeppd : Get sleep period configuration + iwpriv mlan0 sleeppd 10 : Set sleep period to 10 ms + +pscfg + This command is used to set/get PS configuration parameters. + + Usage: + iwpriv mlanX pscfg [k] [d] [l] ... + + Where the parameters: + [k]: Keep alive null packet interval (0: Unchanged, -1: Disable, n: Interval in seconds) + [d]: DTIM interval ( 0: Unchanged, + 1-5: Value, + 65534: DTIM will be ignored, listen interval will be used, + 65533: Closest DTIM to the listen interval period will be used ) + [l]: Local listen interval ( 0: Unchanged, + -1: Disable, + 1-49: Value in beacon intervals, + >= 50: Value in TUs ) + [a]: Ad-hoc awake period (0: Unchanged, 1-31: Beacon interval, 255: Firmware + will go to sleep after beacon send out) + [b]: Beacon miss timeout (0: Unchanged, 1-50: Value in milliseconds, 65535: Disable) + [p]: Delay to PS (0-65535: Value in milliseconds, default 1000ms) + [m]: PS mode (0: Unchanged, 1: Auto mode, 2: PS-Poll mode, 3: PS Null mode) + No change if parameters are not provided. + + Examples: + iwpriv mlan0 pscfg : Get all the current PS configuration settings + iwpriv mlan0 pscfg 3 4 : Set PS keep alive null packet interval to 3 seconds + and DTIM interval to 4, all the other configurations + are unchanged + iwpriv mlan0 pscfg 0 0xfffe 10 0 20 + : Disable DTIM interval, set local listen interval to + 10 beacon intervals and beacon miss interval to 20, + all the other configurations are unchanged + iwpriv mlan0 pscfg 0 0 0 0 0 50 : Set delay to PS to 50 ms, keep the others unchanged + +fwwakeupmethod + This command is used to set/get the firmware wakeup method. + + where value is: + [n]: + 1 -- Firmware wakeup through the interface command interrupt + -- (default setting for SDIO/PCIe/USB) + 2 -- Firmware wakeup through the GPIO pin + [g]:If firware wakeup throug GPIO pin, [g] is GPIO pin number + + Examples: + iwpriv mlan0 fwwakeupmethod : Get current firmware wakeup method + iwpriv mlan0 fwwakeupmethod 1 : Set firmware wakeup method to Interface mode + iwpriv mlan0 fwwakeupmethod 2 5 : Set firmware wakeup method to GPIO mode, GPIO_pin=5 + +getkey + This command is used to get PTK/GTK + + iwpriv mlanX getkey +associate + Request an association to a given SSID/BSSID pair. This the only accurate + way to pick a specific AP and ESS for an association. The entry must + already exist in the scan table for the association to be attempted. + + iwpriv mlanX associate "xx:xx:xx:xx:xx:xx SSID" + +sleepparams + This command is used to set the sleepclock configurations + + Usage: + iwpriv mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>] + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + + Examples: + iwpriv mlan0 sleepparams : Get current sleepclock configuration + iwpriv mlan0 sleepparams 10 1000 2000 1 0 128 : Set sleepclock configuration + +netmon + This command is used to set/get sniffer mode configuration. + Note: The channel and band config is optional. If not specified, or if + any STA/uAP/STA+uAP connection is active, sniffer activity will be started + on the current config set in the FW. + 'rtap' monitor interface will be created on enabling sniffer activity and + should be made 'up' for capturing in a sniffer app. + + Usage: + iwpriv <interface> netmon [<act> [<filter>]] + iwpriv <interface> netmon [<act> [<filter>] [<band> <chan> [offset]]] + + Where the parameters are: + <interface> : mlanX, uapX + <act> : (1/0) enable/disable sniffer activity + <filter> : network monitor filer flag + bit 0: (1/0) enable/disable management frame + bit 1: (1/0) enable/disable control frame + bit 2: (1/0) enable/disable data frame + bit 3: (1/0) enable/disable frames destined to active connection only + bit 4: (1/0) enable/disable decrypted unicast data/mgmt frames + <band> : 802.11 band + bit 0: B + bit 1: G + bit 2: A + bit 3: GN + bit 4: AN + bit 5: AC 2.4G + bit 6: AC 5G + <chan> : channel to monitor + [offset] : secondary channel bandwidth + 0 - Bandwidth 20Mhz + 1 - HT Bandwidth 40Mhz sec channel above + 3 - HT Bandwidth 40Mhz sec channel below + 4 - VHT Bandwidth 80Mhz + + Examples: + iwpriv mlan0 netmon : Get the current sniffer mode configuration + iwpriv mlan0 netmon 0 : Disable network monitor activity + iwpriv uap0 netmon 1 7 : Enable sniffer activity on current channel set in FW, + set filter data, control, management frame. + iwpriv mlan0 netmon 1 4 11 6 : Enable sniffer activity in absence of active connection, + set filter data frame, band B/G/GN and channel 6 + iwpriv mlan0 netmon 1 7 20 64 1 : Enable sniffer activity in absence of active connection, + set filter management, control and data frame, band A/AN, + channel 64 and secondary channel above + iwpriv uap0 netmon 1 0x0c : Enable sniffer activity, set filter data frames + destined to the active uAP connection only + + iwpriv mlan0 netmon 1 0x1d : Enable sniffer activity, set filter decrypted data and + management frames destined to the active STA connection + only + +authtype + This command is used to set/get authentication type. + + Usage: + iwpriv mlanX authtype [n] + + where <n> + 0: 802.11 open system authentication + 1: 802.11 shared key authentication + 255: allow open system or shared key authentication (default) + + Examples: + iwpriv mlan0 authtype 0 : use open system authentication + iwpriv mlan0 authtype 1 : use shared key authentication + iwpriv mlan0 authtype 255 : allow open system or shared key authentication + iwpriv mlan0 authtype : get current setting + +powercons + This command is used to set the local transmit power constraint. + Value is in dbm unit. This command is only used for ad-hoc start. + + Usage: + iwpriv mlanX powercons [n] + + Examples: + iwpriv mlanX powercons : get the current setting + iwpriv mlanX powercons 12 : set local power constraint to 12 dbm + +htstreamcfg + This command is used to set/get HT stream configuration. + The setting only takes effect in next association. + + Usage: + iwpriv mlanX htstreamcfg [n] + + where <n> + 0x11: HT stream 1x1 mode + 0x22: HT stream 2x2 mode + + Examples: + iwpriv mlan0 htstreamcfg : Get current setting + iwpriv mlan0 htstreamcfg 0x11 : Set HT stream 1x1 mode + iwpriv mlan0 htstreamcfg 0x22 : Set HT stream 2x2 mode + +ipaddr + This command is used to set/get IP address. + + Usage: + iwpriv mlanX ipaddr ["<op>;<ipaddr>"] + + where <op> + 0: Remove the IP address + bit 0: Set IP address for broadcast ARP filter, which will be auto enabled + in next host sleep configuration + bit 1: Set IP address for auto broadcast ARP response + + Examples: + iwpriv mlan0 ipaddr : Get current settings + iwpriv mlan0 ipaddr "0" : Remove IP address + iwpriv mlan0 ipaddr "1;192.168.0.5" : Set IP address for ARP filter + iwpriv mlan0 ipaddr "3;192.168.0.6" : Set IP address for ARP filter + : and auto ARP response + +macctrl + This command is used to set/get MAC control. + It's recommended to read the current setting first to avoid override issue. + + Usage: + iwpriv mlanX macctrl [n] + + where <n> + bit 0: Rx enabled + bit 1: Tx enabled + bit 3: WEP enabled + bit 4: EthernetII enabled + bit 7: Promiscuous enabled + bit 8: All multicast enabled + bit 9: RTS/CTS enabled (0: CTS to self) + bit 11: Force 11n protection disabled + bit 12: Ad-hoc g protection disabled + ... + + Examples: + iwpriv mlan0 macctrl : Get current MAC control + iwpriv mlan0 macctrl 0x13 : Set Tx/Rx on and EthernetII on + iwpriv mlan0 macctrl 0x813 : Set Tx/Rx on and EthernetII on + : Disable force 11n protection + +dfstesting + This command is used to set/get settings for DFS testing. + + Usage: + iwpriv mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>] + + where <user_cac_pd> is user-configured Channel Availability Check in msec + 0 = disable, use default period (60000) + 1-65535 = enable with that period + where <user_nop_pd> is user-configured Non-Occupancy Period in sec + 0 = disable, use default period (1800) + 1-65535 = enable with that period + where <no_chan_change> is enable/disable no channel change on radar + 0 = disable, 1 = enable (overrides below) + where <fixed_chan_num> is user-configured channel to change to on radar + 0 = disable, 1-255 = enable with that channel + (channel validity for region, etc. is not checked) + (only takes effect if no_chan_change = 0) + + Examples: + iwpriv mlan0 dfstesting : Get current dfstesting settings + iwpriv mlan0 dfstesting 2000 0 0 0 : user_cac=2sec, others disabled/default + iwpriv mlan0 dfstesting 0 0 1 0 : only no_chan_change enabled + iwpriv mlan0 dfstesting 0 120 0 64 : user_nop=2min, force chan 64 on radar + +thermal + This command is used to get the current thermal reading. + + Examples: + iwpriv mlan0 thermal : Get thermal reading + +indrstcfg + This command is used to set/get settings for independent reset mode + + Usage: + iwpriv mlanX indrstcfg <ir_mode> [gpio_pin] + + where <ir_mode> is independent reset mode + 0 = disable independent reset + 1 = enable out band and disable in band + 2 = enable in band and disable out band + where <gpio_pin> is user-configured gpio pin number to be used for oob mode + 0xFF = use FW default gpio assignment + 0-15 = gpio pin number + + Examples: + iwpriv mlan0 indrstcfg : Get current settings + iwpriv mlan0 indrstcfg 1 14 : Enable oob_mode with gpio pin 14 + iwpriv mlan0 indrstcfg 1 0xff : Enable oob_mode with default gpio pin + iwpriv mlan0 indrstcfg 0 : Disable ir_mode + iwpriv mlan0 indrstcfg 2 : Enable in band reset mode + + +=============================================================================== + U S E R M A N U A L F O R M L A N C O N F I G + +NAME +mlanconfig - configure the additional parameters available for the Marvell mdriver. + +SYNOPSIS +mlanconfig -v +mlanconfig <mlanX> <command> [parameters] ... + +mlanconfig mlanX hostcmd <bg_scan.conf> bgscfg +mlanconfig mlanX hostcmd <requesttpc.conf> requesttpc +mlanconfig mlanX hostcmd <crypto_test.conf> crypto_test +mlanconfig mlanX hostcmd <subevent.conf> subevent_get +mlanconfig mlanX hostcmd <subevent.conf> subevent_set +mlanconfig mlanX hostcmd <auto_tx.conf> auto_tx_get +mlanconfig mlanX hostcmd <auto_tx.conf> nat_keep_alive +mlanconfig mlanX hostcmd <auto_tx.conf> auto_tx_unreg +mlanconfig mlanX hostcmd <txrate_cfg.conf> txrate_cfg_get +mlanconfig mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bg +mlanconfig mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bgn +mlanconfig mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_cfg_get +mlanconfig mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_2g_cfg_set +mlanconfig mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_5g_cfg_set +mlanconfig mlanX hostcmd <pad_cfg.conf> pad_cfg_get +mlanconfig mlanX hostcmd <pad_cfg.conf> pad_cfg_set +mlanconfig mlanX hostcmd <11n_2040coex.conf> 2040coex +mlanconfig mlanX hostcmd <robust_btc.conf> mode_get +mlanconfig mlanX hostcmd <robust_btc.conf> mode_timeshare +mlanconfig mlanX hostcmd <robust_btc.conf> mode_spatial +mlanconfig mlanX hostcmd <robust_btc.conf> generictime +mlanconfig mlanX hostcmd <robust_btc.conf> a2dptime +mlanconfig mlanX hostcmd <robust_btc.conf> inquirytim +mlanconfig mlanX hostcmd <robust_btc.conf> ap_generictime +mlanconfig mlanX hostcmd <robust_btc.conf> ap_a2dptime +mlanconfig mlanX hostcmd <robust_btc.conf> ap_inquirytime +mlanconfig mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_get +mlanconfig mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_set +mlanconfig mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_disable +mlanconfig mlanX arpfilter <arpfilter.conf> +mlanconfig mlanX mefcfg <mef.conf> +mlanconfig mlanX cfgdata <register type> <conf file> +mlanconfig mlanX sdcmd52rw <FN no.> <address> [data] +mlanconfig mlanX sdcmd53rw <FN no.> <address> <mode> <blksize> <blknum> [data1] ... [dataN] +mlanconfig mlanX setuserscan [ARGS] +mlanconfig mlanX getscantable [ARGS] +mlanconfig mlanX addts <filename.conf> <section# of tspec> <timeout in ms> +mlanconfig mlanX delts <filename.conf> <section# of tspec> +mlanconfig mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3] +mlanconfig mlanX qconfig get [Queue Id: 0-3] +mlanconfig mlanX qconfig def [Queue Id: 0-3] +mlanconfig mlanX qstats on [Queue Id: 0-3] +mlanconfig mlanX qstats off [Queue Id: 0-3] +mlanconfig mlanX qstats get [Queue Id: 0-3] +mlanconfig mlanX qstatus +mlanconfig mlanX ts_status +mlanconfig mlanX regrdwr <type> <offset> [value] +mlanconfig mlanX memrdwr <address> [value] +mlanconfig mlanX customie <index> <mask> <IE buffer> +mlanconfig mlanX tdls_config <0/1> +mlanconfig mlanX tdls_setinfo <tdls.conf> +mlanconfig mlanX tdls_setup <tdls.conf> +mlanconfig mlanX tdls_discovery <tdls.conf> +mlanconfig mlanX tdls_teardown <tdls.conf> +mlanconfig mlanX tdls_powermode <tdls.conf> +mlanconfig mlanX tdls_channel_switch <tdls.conf> +mlanconfig mlanX tdls_stop_channel_switch <tdls.conf> +mlanconfig mlanX tdls_cs_params <tdls.conf> +mlanconfig mlanX tdls_disable_cs <0/1> +mlanconfig mlanX tdls_link_status +mlanconfig mlanX tdls_debug "wrong_bss" <0/1> +mlanconfig mlanX tdls_debug "setup_existing_link" <0/1> +mlanconfig mlanX tdls_debug "fail_setup_confirm" <0/1> +mlanconfig mlanX tdls_debug "setup_with_prohibited" <0/1> +mlanconfig mlanX tdls_debug "higher_lower_mac" <0/1> +mlanconfig mlanX tdls_debug "ignore_key_expiry" <0/1> +mlanconfig mlanX tdls_debug "allow_weak_security" <0/1> +mlanconfig mlanX tdls_debug "stop_rx" <0/1> +mlanconfig mlanX tdls_debug "cs_im_return" <0/1> +mlanconfig mlanX mgmtframetx <mgmt_frame.conf> + + +DESCRIPTION + +Those commands are used in Marvell specific application called mlanconfig. + +=========== +-v + This command is used to display the version of mlanconfig utility. + Usage: + mlanconfig -v + +hostcmd bgscfg + This command is used to configure the various parameters for PPS/UAPSD + or normal background scan. + + Usage: + mlanconfig mlanX hostcmd config/bg_scan.conf bgscfg + +hostcmd requesttpc + This command is used to request 802.11H TPC info. + + Usage: + mlanconfig mlanX hostcmd config/requesttpc.conf requesttpc + +hostcmd crypto_test + This command is used to test the encryption/decryption API of the firmware. + + Usage: + mlanconfig mlanX hostcmd config/crypto_test.conf crypto_test + +hostcmd subevent_get +hostcmd subevent_set + This command is used to get/set the configurations for event descriptor + interface command. + subsvent_get: get subscribed event parameters + subsvent_set: set subscribed event parameters + + Usage: + mlanconfig mlanX hostcmd config/subevent.conf subevent_get + mlanconfig mlanX hostcmd config/subevent.conf subevent_set + +hostcmd auto_tx_get +hostcmd nat_keep_alive +hostcmd auto_tx_unreg + This command is used to configures the Frame Auto Transmission parameters. + auto_tx_get: get auto_tx parameters + nat_keep_alive: register to firmware for sending NAT Keep Alive packet + auto_tx_unreg: unregister to firmware auto_tx + + Usage: + mlanconfig mlanX hostcmd config/auto_tx.conf auto_tx_get + mlanconfig mlanX hostcmd config/auto_tx.conf nat_keep_alive + mlanconfig mlanX hostcmd config/auto_tx.conf auto_tx_unreg + +hostcmd txrate_cfg_get +hostcmd txrate_cfg_set_bg +hostcmd txrate_cfg_set_bgn + This command is used to set/get the transmit data rate. + + Usage: + mlanconfig mlanX hostcmd config/txrate_cfg.conf txrate_cfg_get + mlanconfig mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bg + mlanconfig mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bgn + +hostcmd txpwrlimit_cfg_get +hostcmd txpwrlimit_2g_cfg_set +hostcmd txpwrlimit_5g_cfg_set + This command is used to set/get the configuration data of Tx power limitation. + Note: The configuration set should be issued when STA is disconnected. + + Usage: + mlanconfig mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_cfg_get + mlanconfig mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_2g_cfg_set + mlanconfig mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_5g_cfg_set + +hostcmd pad_cfg_get +hostcmd pad_cfg_set + This command is used to set/get the configuration data for PAD OR. + + Usage: + mlanconfig mlanX hostcmd config/pad_cfg.conf pad_cfg_get + mlanconfig mlanX hostcmd config/pad_cfg.conf pad_cfg_set + +hostcmd 2040coex + This command is used to send the 11n 20/40 Coex command to firmware. + Firmware will send 11n 20/40 Coex management action frame to AP. + + Usage: + mlanconfig mlanX hostcmd config/11n_2040coex.conf 2040coex + +hostcmd mode_get +hostcmd mode_timeshare +hostcmd mode_spatial + This command is used to get/set Robust BT Coex. + mode_get: get the current mode + mode_timeshare: set Robust BT Coex to timeshare mode (default on 1x1 chips) + mode_spatial: set Robust BT Coex to spatial mode (only for, and default on 2x2 chips) + + Usage: + mlanconfig mlanX hostcmd config/robust_btc.conf mode_get + mlanconfig mlanX hostcmd config/robust_btc.conf mode_timeshare + mlanconfig mlanX hostcmd config/robust_btc.conf mode_spatial + +hostcmd generictime +hostcmd a2dptime +hostcmd inquirytime +hostcmd ap_generictime +hostcmd ap_a2dptime +hostcmd ap_inquirytime + This command is used to configure the time slice of COEX (only works in timeshare mode) + generictime: configure the Bttime and Wlantime in Station Generic case + a2dptime: configure the Bttime and Wlantime in Station A2DP case + inquirytime: configure the Bttime and Wlantime in Station Inquiry case + ap_generictime: configure the Bttime and Wlantime in Ap Generic case + ap_a2dptime: configure the Bttime and Wlantime in Ap A2DP case + ap_inquirytime: configure the Bttime and Wlantime in Ap Inquiry case + + Usage: + mlanutl mlanX hostcmd config/robust_btc.conf generictime + mlanutl mlanX hostcmd config/robust_btc.conf a2dptime + mlanutl mlanX hostcmd config/robust_btc.conf inquirytim + mlanutl mlanX hostcmd config/robust_btc.conf ap_generictime + mlanutl mlanX hostcmd config/robust_btc.conf ap_a2dptime + mlanutl mlanX hostcmd config/robust_btc.conf ap_inquirytime + +hostcmd sdio_pulldown_get +hostcmd sdio_pulldown_set +hostcmd sdio_pulldown_disable + This command is used to set/get the settings of pulling up and + pulling down of SDIO lines. + + Usage: + mlanconfig mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_get + mlanconfig mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_set + mlanconfig mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_disable + +arpfilter + This command is used to configure the ARP filtering parameters. + + Usage: + mlanconfig mlanX arpfilter config/arpfilter.conf + +mefcfg + This command is used to set MEF settings. + + Usage: + mlanconfig mlanX mefcfg config/mef.conf + +cfgdata + This command is used to set/get the configuration data to/from firmware. + + Usage: + mlanconfig mlanX cfgdata <type> <.conf file name> + This command is used to set the cfg data in the .conf file to firmware. + + mlanconfig mlanX cfgdata <type> + This command is used to get the cfg data from firmware and display + on to the console. + + Where the value of <type> field is: + 2 -- Cal data download and <.conf file name> is cal_data.conf + +sdcmd52rw + This command is used to read/write a controller register in + Secure Digital I/O Interfaces. + + Usage: + mlanconfig mlanX sdcmd52rw <function number> <register address> [value] + + For SDIO MMC driver, only function 0 and 1 access is allowed. And there + is a limitation for function 0 write, only vendor specific CCCR registers + (0xf0 -0xff) are permiited. + + Examples: + mlanconfig mlan0 sdcmd52rw 1 3 + mlanconfig mlan0 sdcmd52rw 1 1 0x3f + +sdcmd53rw + This command is used to issue a CMD53 read/write data in + Secure Digital I/O Interfaces. + + Usage: + mlanconfig mlanX sdcmd53rw <func> <address> <mode> <blksize> <blknum> [data1] ... [dataN] + + where the parameters are, + <func>: function number (0/1/2/..) + <address>: data address + <mode>: byte mode/block mode (0/1) + <blksize>: block size (32/64/../512, NA for byte mode) + <blknum>: block number or byte number + <data1> ... <dataN>: data for write + + Note: The total data length is block size * block number for block mode + or byte number for byte mode. The max data length is 2000-byte. + For write the data pattern will be duplicated to data buffer. + + Examples: + mlanconfig mlan0 sdcmd53rw 0 0x8000 1 0x40 2 + mlanconfig mlan0 sdcmd53rw 1 0x10000 0 1 5 0x0a 0x0b 0x0c 0x0d 0x0e + +setuserscan + Initiate a customized scan and retrieve the results + + Usage: + mlanconfig mlanX setuserscan [ARGS] + + Where [ARGS]: + ssid="[SSID]" specify a SSID filter for the scan + chan=[chan#][band][mode] where band is [a,b,g,n] and mode is + blank for active or 'p' for passive + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + wc="[WILDCARD SSID]" specify a UNIX pattern matching filter (using * + and ?) for SSIDs found in a broadcast probe + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + gap=[time gap] Time gap between two scans in milliseconds + probes=[#] number of probe requests to send on each chan + for each broadcast probe required and each SSID + specific probe required (1-4) + type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + + Any combination of the above arguments can be supplied on the command line. + If the chan token is absent, a full channel scan will be completed by driver. + If the dur or probes tokens are absent, the driver default setting will be + used. The bssid and ssid fields, if blank, will produce an unfiltered scan. + It's allowed to input multiple ssid/wc entries, the max entry number is 10. + The type field will default to 3 (Any) and the keep field will default to 0 + (Discard). + + Examples: + 1) Perform an active scan on channels 1, 6, and 11 in the 'g' band: + setuserscan chan=1g,6g,11g + + 2) Perform a passive scan on channel 11 for 20 ms: + setuserscan chan=11gp dur=20 + + 3) Perform an active scan on channels 1, 6, and 11; and a passive scan on + channel 36 in the 'a' band: + setuserscan chan=1g,6g,11g,36ap + + 4) Perform an active scan on channel 6 and 36 for specific SSID: + setuserscan chan=6g,36a ssid=TestAP1 ssid=TestAP2 + + 5) Scan all available channels (B/G/N, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + setuserscan bssid=00:50:43:20:12:82 keep=1 + + 6) Scan channel 6, for all infrastructure networks, sending two probe + requests. Keep the previous scan table intact. Update any duplicate + BSSID/SSID matches with the new scan data: + setuserscan chan=6g type=1 probes=2 keep=1 + + 7) Scan channel 1 and 6, for all networks matching the Mrvl*AP + or AP*Mrvl? patterns and for MrvlTst SSID. Generate 3 broadcast + probes for the patterns and 3 SSID specific probes for MrvlTst on + both channel 1 and channel 6. + setuserscan chan=1g,6g probes=3 wc="Mrvl*AP" wc="AP*Mrvl?" ssid="MrvlTst" + + 8) Scan all the channels for specified band. + setuserscan chan=0g + + 9) Scan channel 1 and 6, send 3 probe requests, scan each channel for 40 ms + with time gap of 50ms between 2 scans + setuserscan chan=1g,6g probes=3 dur=40 gap=50 + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. + +getscantable + Display the current contents of the driver scan table + + Usage: + mlanconfig mlanX getscantable + mlanconfig mlanX getscantable [#] + mlanconfig mlanX getscantable tsf + mlanconfig mlanX getscantable help + + 1) Without argument, the entire scantable is displayed. + 2) Specifying a # will display detailed information about a specific scan + table entry. '0' displays driver cached information regarding the + current association (if any). + 3) The tsf argument will display the entire scan table with the recorded + TSF timestamp for the entry. + 4) The help argument will display the legend for the capability field. + +addts + Send an ADDTS command to the associated AP. + + Process a given conf file for a specific TSPEC data block. Send the + TSPEC along with any other IEs to the driver/firmware for transmission + in an ADDTS request to the associated AP. + + Return the execution status of the command as well as the ADDTS response + from the AP if any. + + Usage: + mlanconfig mlanX addts <filename.conf> <section# of tspec> <timeout(ms)> + +delts + Send a DELTS command to the associated AP. + + Process a given conf file for a specific TSPEC data block. Send the + TSPEC along with any other IEs to the driver/firmware for transmission + in a DELTS request to the associated AP. + + Return the execution status of the command. There is no response to a + DELTS from the AP. + + Usage: + mlanconfig mlanX delts <filename.conf> <section# of tspec> + +qconfig + Send a WMM AC Queue configuration command to get/set/default params + + Configure or get the parameters of a WMM AC queue. The command takes + an optional Queue Id as a last parameter. Without the queue id, all + queues will be acted upon. + + Usage: + mlanconfig mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3] + mlanconfig mlanX qconfig get [Queue Id: 0-3] + mlanconfig mlanX qconfig def [Queue Id: 0-3] + +qstats + Turn on/off or retrieve and clear the queue statistics for an AC + + Turn the queue statistics collection on/off for a given AC or retrieve the + current accumulated stats and clear them from the firmware. The command + takes an optional Queue Id as a last parameter. Without the queue id, + all queues will be acted upon. + + Usage: + mlanconfig mlanX qstats on [Queue Id: 0-3] + mlanconfig mlanX qstats off [Queue Id: 0-3] + mlanconfig mlanX qstats get [Queue Id: 0-3] + +qstatus + This command retrieves the current status of the WMM queues. If WMM + is enabled then it displays the information for each AC in a table. + + Usage: + mlanconfig mlanX qstatus + +ts_status + This command queries the FW for the status of TSIDs 0 through 7 + configured via call admission control and displays the results in a + table. + + Usage: + mlanconfig mlanX ts_status + +regrdwr + This command is used to read/write the adapter register. + + Usage: + mlanconfig mlanX regrdwr <type> <offset> [value] + + where the parameters are, + <type>: 1:MAC/SOC, 2:BBP, 3:RF, 5:CAU + <offset>: offset of register + [value]: value to be written + + Examples: + mlanconfig mlan0 regrdwr 1 0xa060 : Read the MAC register + mlanconfig mlan0 regrdwr 1 0xa794 0x80000000 + : Write 0x80000000 to MAC register + +memrdwr + This command is used to read/write the adapter memory. + + Usage: + mlanconfig mlanX memrdwr <address> [value] + + where the parameters are, + <address>: memory address + [value]: value to be written + + Examples: + mlanconfig mlan0 memrdwr 0x80000000 : Read memory address 0x80000000 + mlanconfig mlan0 memrdwr 0x80000000 0xffffffff + : Write 0xffffffff to memory address 0x80000000 + +customie + This command is used to set or get custom IEs for management frames. + + Usage : customie [INDEX] [MASK] [IEBuffer] + empty - Get all IE settings + INDEX: 0 - Get/Set IE index 0 setting + 1 - Get/Set IE index 1 setting + 2 - Get/Set IE index 2 setting + MAX IE Index depends on device memory. + + -1 - Append/Delete IE automatically + Delete will delete the IE from the matching IE buffer + Append will append the IE to the buffer with the same mask + MASK : Management subtype mask value as per bit definitions + : Bit 0 - Association request + : Bit 1 - Association response + : Bit 2 - Reassociation request + : Bit 3 - Reassociation response + : Bit 4 - Probe request + : Bit 5 - Probe response + : Bit 8 - Beacon + MASK : MASK = 0 to clear the mask and the IE buffer + IEBuffer : IE Buffer in hex (max 256 bytes) + The Buffer should not be space separated. + + Example: + ./mlanconfig mlan0 customie + Get IE buffer, subtype mask settings for all indices. + + ./mlanconfig mlan0 customie 1 + Get IE buffer and subtype mask for the Index = 1. + + ./mlanconfig mlan0 customie 2 0 + Clear IE buffer and mask value for Index = 2. + + ./mlanconfig mlan0 customie 3 0x101 0xdd051234567890 + Set IE buffer and mask value for Index = 3. + + ./mlanconfig mlan0 customie -1 0x101 0xdd051234567890 + Append the specified IEBuffer at index with mask value of 0x101. + + ./mlanconfig mlan0 customie -1 0 0xdd051234567890 + Delete the specified IEBuffer from all the IEs. + + ./mlanconfig mlan0 customie 2 0 0xdd051234567890 + Delete the specified IEBuffer from the IEs at index 2. + +tdls_config + This command is used to enable/disable TDLS on device. + + Usage : tdls_config <0/1> + 1: Enable TDLS + 0: Disable TDLS +tdls_setinfo + This command is used for setting the capabilities of the TDLS station. + + Usage : tdls_setinfo <tdls.conf> + Set capabilities of TDLS station. +tdls_setup + This command is used to send TDLS setup request. + + Usage: tdls_setup <tdls.conf> + Send TDLS setup request. +tdls_discovery + This command is used to request TDLS discovery. + + Usage : tdls_discovery <tdls.conf> + Request TDLS discovery. +tdls_teardown + This command is used to send TDLS teardown request. + + Usage : tdls_teardown <tdls.conf> + Request teardown of TDLS link. +tdls_powermode + This command is used to send TDLS powermode request. + + Usage : tdls_powermode <tdls.conf> + Send TDLS powermode (either 0:Active, 1:PowerSave) command. +tdls_channel_switch + This command is used to send TDLS channel switch request. + + Usage : tdls_channel_switch <tdls.conf> + Send TDLS channel switch command. +tdls_stop_channel_switch + This command is used to send stop TDLS channel switch request. + + Usage : tdls_stop_channel_switch <tdls.conf> + Send stop TDLS channel switch command. +tdls_cs_params + This command is used to set TDLS channel switch params + + Usage : tdls_cs_params <tdls.conf> + Set TDLS channel switch params. +tdls_disable_cs + This command is used to disable TDLS channel switch + + Usage : tdls_disable_cs <0/1> + Disable TDLS channel switch. +tdls_link_status [peer_mac_address] + This command is used to get link information about TDLS links or + a TDLS link correponding to peer mac address. + + Usage : tdls_link_status + Send TDLS command to get current link status. +tdls_debug + This command is used for FW debug functionality and tests. + +mgmtframetx + This command is used to send mgmt frame + + Usage: + mlanconfig mlanX mgmtframetx <mgmt_frame.conf> + + +=============================================================================== + U S E R M A N U A L F O R M L A N 2 0 4 0 C O E X + +NAME +mlan2040coex - This application handles the 11n 20/40 coexistence operation for + the Marvell mdriver + +SYNOPSIS +mlan2040coex [-d <n>] [-i <intfname>] [hvB] + -d = Device number (n) + -i = Interface name (intfname) + -h = Help + -v = Version + -B = Run the process in background +Note: If device number (n) is not present, then 0 assumed. + If intfname is not present, then mlan0 assumed. + +=============================================================================== + U S E R M A N U A L F O R MLANEVENT + +NAME +mlanevent.exe + +This tool can be used to listen for and obtain events from the driver +through the netlink layer. + +---------------- +Supported events +---------------- +STA_DEAUTH +STA_ASSOC +BSS_START +BSS_IDLE +BSS_ACTIVE + +----------------- +Details of events +----------------- + +STA_DEAUTH +---------- + For this event, the following information is shown: + + Deauthenticated STA MAC address. + + Reason for deauthentication. + +STA_ASSOC +---------- + For this event, the following information is shown: + + STA MAC address. + +BSS_START +---------- + For this event, the following information is shown: + + AP MAC address. + +BSS_IDLE +---------- + For this event, there is no associated information. + +BSS_ACTIVE +---------- + For this event, there is no associated information. + +===============================================================================
diff --git a/wlan_sd8897/README_MLAN b/wlan_sd8897/README_MLAN new file mode 100644 index 0000000..47c1d62 --- /dev/null +++ b/wlan_sd8897/README_MLAN
@@ -0,0 +1,3637 @@ +=============================================================================== + U S E R M A N U A L + + Copyright (C) 2008-2018, Marvell International Ltd. + All Rights Reserved + +1) FOR DRIVER BUILD + + Goto source code directory wlan_src/. + make [clean] build + The driver and utility binaries can be found in ../bin_xxxx directory. + The driver code supports Linux kernel up to 4.15. + +2) FOR DRIVER INSTALL + + a) Copy firmware image sd8787_uapsta.bin | sd8797_uapsta.bin | ... to + /lib/firmware/mrvl/ directory, create the directory if it doesn't exist. + b) Install WLAN driver, + There are drv_mode, max_sta_bss, max_uap_bss etc. module parameters. + The bit settings of drv_mode are, + Bit 0 : STA + Bit 1 : uAP + Bit 2 : WIFIDIRECT + The default drv_mode is 7. + Bit 4 : NAN + + max_sta_bss: Maximum number of STA BSS (default 1, max 1) + sta_name: Name of the STA interface (default: "mlan") + max_uap_bss: Maximum number of uAP BSS (default 1, max 2) + uap_name: Name of the uAP interface (default: "uap") + max_wfd_bss: Maximum number of WIFIDIRECT BSS (default 1, max 1) + wfd_name: Name of the WIFIDIRECT interface (default: "wfd") + max_vir_bss: Number of Virtual interfaces (default 0) + nan_name: Name of the NAN interface (default: "nan") + max_nan_bss: Number of NAN interfaces (default 1) + For example, to install SD8787 driver, + insmod mlan.ko + insmod sd8787.ko [drv_mode=3] [fw_name=mrvl/sd8787_uapsta.bin] + To load driver in STA only mode, + insmod mlan.ko + insmod sd8787.ko drv_mode=1 [fw_name=mrvl/sd8787_uapsta.bin] + To load driver in uAP only mode, + insmod mlan.ko + insmod sd8787.ko drv_mode=2 [fw_name=mrvl/sd8787_uapsta.bin] + + To switch mode between STA only, uAP only and uAPSTA etc. in run time, + echo drv_mode=1 > /proc/mwlan/config // STA mode + echo drv_mode=2 > /proc/mwlan/config // uAP mode + echo drv_mode=3 > /proc/mwlan/config // STA+uAP mode + echo drv_mode=7 > /proc/mwlan/config // STA+uAP+WIFIDIRECT mode + c) Uninstall WLAN driver, + ifconfig mlanX down + ifconfig uapX down + rmmod sd8xxx + rmmod mlan + + To load driver with MFG firmware file, use mfg_mode=1 when insmod WLAN driver and + specify MFG firmware name if needed. + + There are some other parameters for debugging purpose etc. Use modinfo to check details. + drvdbg=<bit mask of driver debug message control> + dev_cap_mask=<Bit mask of the device capability> + mac_addr=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)> + auto_ds=0|1|2 <use MLAN default | enable auto deepsleep | disable auto deepsleep> + ps_mode=0|1|2 <use MLAN default | enable IEEE PS mode | disable IEEE PS mode> + p2p_enh=0|1 <Disable enhanced P2P (default) | Enable enhanced P2P> + max_tx_buf=2048|4096|8192 <maximum AMSDU Tx buffer size> + pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend> + shutdown_hs=1|0 <Enable HS when shutdown | No HS when shutdown (default)> + cfg_11d=0|1|2 <use MLAN default | enable 11d | disable 11d> + dts_enable=0|1 <Disable DTS | Enable DTS (default)> + hw_test=0|1 <Disable hardware test (default) | Enable hardware test> + fw_serial=0|1 <support parallel download FW | support serial download FW (default)> + req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API> + SD8887: antcfg=0|1|2|0xffff <default | Tx/Rx antenna 1 | Tx/Rx antenna 2 | enable antenna diversity> + SD8897/SD8997: antcfg=0x11|0x13|0x33 <Bit0:Rx Path A, Bit1:Rx Path B, Bit 4:Tx Path A, Bit 5:Tx Path B> + init_cfg=<init config (MAC addresses, registers etc.) file name> + e.g. copy init_cfg.conf to firmware directory, init_cfg=mrvl/init_cfg.conf + cal_data_cfg=<CAL data config file name> + e.g. copy cal_data.conf to firmware directory, cal_data_cfg=mrvl/cal_data.conf + Note: Loading driver with 8887 must include correct cal_data_cfg parameter. + txpwrlimit_cfg=<Tx power limit config file name> + e.g. copy txpwrlimit_cfg_set.conf to firmware directory, txpwrlimit_cfg=mrvl/txpwrlimit_cfg_set.conf + init_hostcmd_cfg=<init hostcmd config file name> + e.g. copy init_hostcmd_cfg.conf to firmware directory, init_hostcmd_cfg=mrvl/init_hostcmd_cfg.conf + cfg80211_wext=<bit mask of CFG80211 and WEXT control> + Bit 0: STA WEXT + Bit 1: uAP WEXT + Bit 2: STA CFG80211 + Bit 3: uAP CFG80211 + cfg80211_drcs=1|0 <Enable DRCS support (default) | Disable DRCS support> + reg_alpha2=<Regulatory alpha2 (default NULL)> + wq_sched_prio: Priority for work queue + wq_sched_policy: Scheduling policy for work queue + (0: SCHED_NORMAL, 1: SCHED_FIFO, 2: SCHED_RR, 3: SCHED_BATCH, 5: SCHED_IDLE) + Please note that, both wq_sched_prio and wq_sched_policy should be provided + as module parameters. If wq_sched_policy is (0, 3 or 5), then wq_sched_prio + must be 0. wq_sched_prio should be 1 to 99 otherwise. + rx_work=0|1|2 <default | Enable rx_work_queue | Disable rx_work_queue> + low_power_mode_enable=0|1 <disable low power mode (default)| enable low power mode> + When low power mode is enabled, the output power will be clipped at ~+10dBm and the + expected PA current is expected to be in the 80-90 mA range for b/g/n modes + indication_gpio=0xXY <GPIO to indicate wakeup source and its level; high four bits X: + level(0/1) for normal wakeup; low four bits Y: GPIO pin number. This parameter + only works with specific board and firmware.> + napi=0|1 <disable napi | enable napi> + Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used + for module parameters. + insmod sd8xxx.ko "<para1> <para2> ..." + +3) FOR DRIVER PROC & DEBUG + + The following info are provided in /proc/net/mwlan/mlanX|uapX|wfdX/info, + on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX|uapX|wfdX/info. + + driver_name = "wlan" or "uap" + driver_version = <chip id, firmware version and driver version> + interface_name = "mlanX", "uapX" or "wfdX" + bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown" + media_state = "Disconnected" | "Connected" + mac_address = <6-byte adapter MAC address> + multicase_count = <multicast address count> // Only for STA + essid = <current SSID> // Only for STA + bssid = <current BSSID> // Only for STA + channel = <current channel> // Only for STA + region_code = <current region code> // Only for STA + multicast_address[n] = <multicast address> // Only for STA + num_tx_bytes = <number of bytes sent to device> + num_rx_bytes = <number of bytes received from device and sent to kernel> + num_tx_pkts = <number of packets sent to device> + num_rx_pkts = <number of packets received from device and sent to kernel> + num_tx_pkts_dropped = <number of Tx packets dropped by driver> + num_rx_pkts_dropped = <number of Rx packets dropped by driver> + num_tx_pkts_err = <number of Tx packets failed to send to device> + num_rx_pkts_err = <number of Rx packets failed to receive from device> + carrier "on" | "off" + tx queue "stopped" | "started" + tkip_mic_failures = 0 // Only for uAP + ccmp_decrypt_errors = 0 // Only for uAP + wep_undecryptable_count = 0 // Only for uAP + wep_icv_error_count = 0 // Only for uAP + decrypt_failure_count = 0 // Only for uAP + mcast_tx_count = 0 // Only for uAP + failed_count = 0 // Only for uAP + retry_count = 0 // Only for uAP + multiple_retry_count = 0 // Only for uAP + frame_duplicate_count = 0 // Only for uAP + rts_success_count = 0 // Only for uAP + rts_failure_count = 0 // Only for uAP + ack_failure_count = 0 // Only for uAP + rx_fragment_count = 0 // Only for uAP + mcast_rx_frame_count = 0 // Only for uAP + fcs_error_count = 0 // Only for uAP + tx_frame_count = 0 // Only for uAP + rsna_tkip_cm_invoked = 0 // Only for uAP + rsna_4way_hshk_failures = 0 // Only for uAP + + The following debug info are provided in /proc/net/mwlan/mlanX|uapX|wfdX/debug, + on kernel 2.6.24 or later, the entry is /proc/mwlan/mlanX|uapX|wfdX/debug. + + drvdbg = <bit mask of driver debug message control> + wmm_ac_vo = <number of packets sent to device from WMM AcVo queue> + wmm_ac_vi = <number of packets sent to device from WMM AcVi queue> + wmm_ac_be = <number of packets sent to device from WMM AcBE queue> + wmm_ac_bk = <number of packets sent to device from WMM AcBK queue> + max_tx_buf_size = <maximum Tx buffer size> + tx_buf_size = <current Tx buffer size> + curr_tx_buf_size = <current Tx buffer size in FW> + ps_mode = <0/1, CAM mode/PS mode> + ps_state = <0/1/2/3, awake state/pre-sleep state/sleep-confirm state/sleep state> + is_deep_sleep = <0/1, not deep sleep state/deep sleep state> // Only for STA + wakeup_dev_req = <0/1, wakeup device not required/required> + wakeup_tries = <wakeup device count, cleared when device awake> + hs_configured = <0/1, host sleep not configured/configured> + hs_activated = <0/1, extended host sleep not activated/activated> + tx_pkts_queued = <number of Tx packets queued> + pps_uapsd_mode = <0/1, PPS/UAPSD mode disabled/enabled> // Only for STA + sleep_pd = <sleep period in milliseconds> // Only for STA + qos_cfg = <WMM QoS info> // Only for STA + tx_lock_flag = <0/1, Tx lock flag> // Only for STA + port_open = <0/1, port open flag> // Only for STA + scan_processing = <0/1, scan processing flag> // Only for STA + num_bridge_pkts = <number of bridged packets> // Only for uAP + num_drop_pkts = <number of dropped packets> // Only for uAP + num_tx_timeout = <number of Tx timeout> + num_cmd_timeout = <number of timeout commands> + timeout_cmd_id = <command id of the last timeout command> + timeout_cmd_act = <command action of the last timeout command> + last_cmd_id = <command id of the last several commands sent to device> + last_cmd_act = <command action of the last several commands sent to device> + last_cmd_index = <0 based last command index> + last_cmd_resp_id = <command id of the last several command responses received from device> + last_cmd_resp_index = <0 based last command response index> + last_event = <event id of the last several events received from device> + last_event_index = <0 based last event index> + num_cmd_h2c_fail = <number of commands failed to send to device> + num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device> + num_tx_h2c_fail = <number of data packets failed to send to device> + num_cmdevt_c2h_fail = <number of commands/events failed to receive from device> + num_rx_c2h_fail = <number of data packets failed to receive from device> + num_int_read_fail = <number of interrupt read failures> + last_int_status = <last interrupt status> + num_evt_deauth = <number of deauthenticated events received from device> // Only for STA + num_evt_disassoc = <number of disassociated events received from device> // Only for STA + num_evt_link_lost = <number of link lost events received from device> // Only for STA + num_cmd_deauth = <number of deauthenticate commands sent to device> // Only for STA + num_cmd_assoc_ok = <number of associate commands with success return> // Only for STA + num_cmd_assoc_fail = <number of associate commands with failure return> // Only for STA + cmd_sent = <0/1, send command resources available/sending command to device> + data_sent = <0/1, send data resources available/sending data to device> + mp_rd_bitmap = <SDIO multi-port read bitmap> + curr_rd_port = <SDIO multi-port current read port> + mp_wr_bitmap = <SDIO multi-port write bitmap> + curr_wr_port = <SDIO multi-port current write port> + cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> + event_received = <0/1, no event to process/event received and yet to process> + ioctl_pending = <number of ioctl pending> + tx_pending = <number of Tx packet pending> + rx_pending = <number of Rx packet pending> + lock_count = <number of lock used> + malloc_count = <number of malloc done> + mbufalloc_count = <number of mlan_buffer allocated> + main_state = <current state of the main process> + sdiocmd53w = <SDIO Cmd53 write status> + sdiocmd53r = <SDIO Cmd52 read status> + hs_skip_count = <number of skipped suspends> + hs_force_count = <number of forced suspends> + + Issue SDIO cmd52 read/write through proc. + Usage: + echo "sdcmd52rw=<func> <reg> [data]" > /proc/mwlan/config + where the parameters: + func: The function number to use (0-7) + reg: The address of the register + data: The value to write, read if the value is absent + For SDIO MMC driver, only function 0 and WLAN function access is allowed. + And there is a limitation for function 0 write, only vendor specific CCCR + registers (0xf0 -0xff) are permiited. + Examples: + echo "sdcmd52rw= 0 4" > /proc/mwlan/config # read func 0 address 4 + cat /proc/mwlan/config # display the register value + echo "sdcmd52rw= 1 3 0xf" > /proc/mwlan/config # write 0xf to func 1 address 3 + + Issue debug_dump command through proc. + Usage: + echo "debug_dump" > /proc/mwlan/config + + Examples: + echo "debug_dump" > /proc/mwlan/config # dump driver internal debug status. + + Use dmesg or cat /var/log/debug to check driver debug messages. + + Update /proc/sys/kernel/printk to change message log levels. + For example, + echo 6 > /proc/sys/kernel/printk (messages with a higher priority than 6 + will be printed to the console) + echo 15 > /proc/sys/kernel/printk (all messages will be printed to console) + +4) FOR FW RELOAD + a) Enable parallel firmware download in driver parameter + insmod sd8xxx.ko fw_serial=0 + + b) default fw name for parallel firmware download + sd8887_wlan_a2.bin + + c) Trigger FW reload + echo "fw_reload=1" > /proc/mwlan/config trigger inband firmware reset and reload firmware + echo "fw_reload=2" > /proc/mwlan/config trigger firmware reload + echo "fw_reload=3" > /proc/mwlan/config set firmware reload flag in driver. + + (Note: This feature will be supported on Robin3 and KF2. + For CAC-A2, it only work with the board which supports parallel fw download) + + + +=============================================================================== + U S E R M A N U A L F O R MLANUTL + +NAME + mlanutl - configure the additional parameters available for Marvell mdriver. + +SYNOPSIS + mlanutl -v + mlanutl <mlanX|uapX|wfdx> <command> [parameters] ... + + mlanutl mlanX 11dcfg + mlanutl mlanX 11dclrtbl + mlanutl mlanX addbapara [<m> <n> <o> <p> <q>] + mlanutl uapX addbapara [<m> <n> <o> <p> <q>] + mlanutl mlanX addbareject [<m0> <m1> ... <m7>] + mlanutl uapX addbareject [<m0> <m1> ... <m7>] + mlanutl mlanX addts <filename.conf> <section# of tspec> <timeout in ms> + mlanutl mlanX adhocaes + mlanutl mlanX aggrpriotbl [<m0> <n0> <m1> <n1> ... <m7> <n7>] + mlanutl uapX aggrpriotbl [<m0> <n0> <m1> <n1> ... <m7> <n7>] + mlanutl mlanX amsduaggrctrl <n> + mlanutl mlanX antcfg [m] [n] + mlanutl mlanX arpfilter <arpfilter.conf> + mlanutl mlanX assocessid <"[essid]"> + mlanutl mlanX assocessid_bssid <"[bssid] [essid]"> + mlanutl mlanX associate "<bssid> <ssid>" + mlanutl mlanX authtype [n] + mlanutl mlanX autotdls [n] + mlanutl mlanX tdls_idle_time [n] + mlanutl uapX dfs_offload [n] + mlanutl mlanX bandcfg [l] [m] [n] [o] + mlanutl mlanX bcninterval [n] + mlanutl wfdX bssrole [l] + mlanutl mlanX cfgdata <register type> [<conf file>] + mlanutl mlanX cfpcode [m] [n] + mlanutl mlanX changraph [<load | anpi | anpiload> <loops>] + mlanutl mlanX coex_rx_winsize [m] + mlanutl mlanX countrycode [l] + + mlanutl mlanX cfpinfo + mlanutl uapX cfpinfo + + mlanutl mlanX acs [<chX> <chY> ... <chZ>] + mlanutl uapX acs [<chX> <chY> ... <chZ>] + + mlanutl mlanX customie [[[<index>] <mask>] <IEBuffer>] + mlanutl mlanX deauth [l] + mlanutl uapX deauth [l] + mlanutl mlanX deepsleep [l] [m] + mlanutl mlanX delba <l> [<m> <n>] + mlanutl uapX delba <l> [<m> <n>] + mlanutl mlanX delts <filename.conf> <section# of tspec> + mlanutl mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>] + mlanutl mlanX drvdbg [n] + mlanutl mlanX esuppmode [l] [m] [n] + mlanutl mlanX extcapcfg [<ext_cap>] + mlanutl mlanX fwmacaddr [mac_addr] + mlanutl mlanX getdatarate + mlanutl uapX getdatarate + mlanutl mlanX getkey + mlanutl mlanX getlog + mlanutl mlanX getscantable [ARGS] + mlanutl mlanX getsignal [m] [n] + mlanutl mlanX signalextcfg [m] + mlanutl mlanX getsignalextv2 [m] + mlanutl mlanX getsignalext [m] + mlanutl mlanX dyn_bw [n] + mlanutl uapX getstalist + mlanutl uapX channel_switch <switch mode> <oper class> <new channel> <switch count> <bandwidth> + mlanutl mlanX hostcmd <11n_2040coex.conf> 2040coex + mlanutl mlanX hostcmd <auto_tx.conf> auto_tx_get + mlanutl mlanX hostcmd <auto_tx.conf> auto_tx_unreg + mlanutl mlanX hostcmd <bg_scan.conf> bgscfg + + mlanutl mlanX hostcmd <pkt_coalescing.conf> coalesce_cfg + mlanutl mlanX hostcmd <ed_mac_ctrl.conf> ed_mac_ctrl + mlanutl mlanX hostcmd <crypto_test.conf> crypto_test + mlanutl mlanX hostcmd <auto_tx.conf> nat_keep_alive + mlanutl mlanX hostcmd <pad_cfg.conf> pad_cfg_get + mlanutl mlanX hostcmd <pad_cfg.conf> pad_cfg_set + mlanutl mlanX hostcmd <requesttpc.conf> requesttpc + mlanutl mlanX hostcmd <robust_btc.conf> mode_get + mlanutl mlanX hostcmd <robust_btc.conf> mode_timeshare + mlanutl mlanX hostcmd <robust_btc.conf> mode_spatial + mlanutl mlanX hostcmd <robust_btc.conf> mode_none + mlanutl mlanX hostcmd <robust_btc.conf> gpio_cfg + mlanutl mlanX hostcmd <robust_btc.conf> generictime + mlanutl mlanX hostcmd <robust_btc.conf> a2dptime + mlanutl mlanX hostcmd <robust_btc.conf> inquirytim + mlanutl mlanX hostcmd <robust_btc.conf> ap_generictime + mlanutl mlanX hostcmd <robust_btc.conf> ap_a2dptime + mlanutl mlanX hostcmd <robust_btc.conf> ap_inquirytime + mlanutl mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_disable + mlanutl mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_get + mlanutl mlanX hostcmd <sdio_pulldown.conf> sdio_pulldown_set + mlanutl mlanX hostcmd <subevent.conf> subevent_get + mlanutl mlanX hostcmd <subevent.conf> subevent_set + mlanutl mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_2g_cfg_set + mlanutl mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_5g_cfg_set + mlanutl mlanX hostcmd <txpwrlimit_cfg.conf> txpwrlimit_cfg_get + mlanutl mlanX hostcmd <txrate_cfg.conf> txrate_cfg_get + mlanutl mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bg + mlanutl mlanX hostcmd <txrate_cfg.conf> txrate_cfg_set_bgn + mlanutl mlanX hostcmd <conf> generate_raw <raw_data_file> + mlanutl mlanX hostcmd <fwdump.conf> fwdump + + mlanutl mlanX hotspotcfg [<bitmap>] + mlanutl mlanX hscfg [condition [[GPIO# [gap]]]] [ind_GPIO# [level]] + mlanutl mlanX hssetpara condition [GPIO# [gap]] [ind_GPIO# [level]] + mlanutl mlanX mgmtfilter <mgmtfilter.conf> + mlanutl mlanX auto_arp [n] + mlanutl mlanX htcapinfo [<m>] [<n>] + mlanutl mlanX htstreamcfg [n] + mlanutl mlanX httxbfcap [cap] + mlanutl mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData]" + mlanutl mlanX httxcfg [<m>] [<n>] + mlanutl mlanX inactivityto <n> <m> <l> [k] + mlanutl mlanX ipaddr ["<op>;<ipaddr>"] + mlanutl mlanX linkstats + mlanutl mlanX listeninterval [l] + mlanutl mlanX macctrl [n] + mlanutl uapX macctrl [n] + mlanutl mlanX mefcfg <mef.conf> + mlanutl mlanX memrdwr <address> [value] + mlanutl mlanX miracastcfg [l] [m] [n] + mlanutl mlanX mgmtframectrl [<mask>] + mlanutl uapX mgmtframectrl [<mask>] + mlanutl mlanX mgmtframetx <mgmt_frame.conf> + mlanutl mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports] + mlanutl mlanX netmon [<act> [<filter> <band> <chan> [offset]]] + mlanutl mlanX offchannel [<l> <m> <n>] + mlanutl mlanX otpuserdata <l> + mlanutl mlanX passphrase [l] + mlanutl mlanX pb_bypass [data_1, data_2, ... data_n] + mlanutl mlanX pmfcfg <mfpc> <mfpr> + mlanutl mlanX port_ctrl [n] + mlanutl mlanX powercons [n] + mlanutl mlanX pscfg [k] [d] [l] ... + mlanutl mlanX bcntimeoutcfg [l] [m] [o] [p] + mlanutl mlanX psmode [l] + mlanutl <interface> robustcoex <gpiocfg> [Enable/Disable] [gpionum] [gpiopolarity] + mlanutl <interface> keep_connect [Enable/Disable] + mlanutl mlanX qconfig def [Queue Id: 0-3] + mlanutl mlanX qconfig get [Queue Id: 0-3] + mlanutl mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3] + mlanutl mlanX qoscfg + mlanutl mlanX qstatus + mlanutl mlanX radioctrl [n] + mlanutl mlanX rdeeprom <offset> <length> + mlanutl mlanX reassoctrl [n] + mlanutl mlanX regioncode [n] + mlanutl mlanX regrdwr <type> <offset> [value] + mlanutl mlanX rejectaddbareq [conditions] + mlanutl uapX rejectaddbareq [conditions] + mlanutl mlanX scancfg [t] [m] [p] [s] [a] [b] [ext] + mlanutl mlanX sdcmd52rw <FN no.> <address> [data] + mlanutl mlanX sdcmd53rw <FN no.> <address> <mode> <blksize> <blknum> [data1] ... [dataN] + mlanutl mlanX sdioclock <n> + mlanutl mlanX setuserscan [ARGS] + mlanutl mlanX cancelscan + mlanutl mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>] + mlanutl mlanX sleeppd [n] + mlanutl mlanX sysclock [clk1] [clk2] [clk3] [clk4] + mlanutl mlanX tcpackenh [l] + mlanutl mlanX tdls_channel_switch <tdls.conf> + mlanutl mlanX tdls_config <0/1> + mlanutl mlanX tdls_cs_params <tdls.conf> + mlanutl mlanX tdls_debug "allow_weak_security" <0/1> + mlanutl mlanX tdls_debug "cs_im_return" <0/1> + mlanutl mlanX tdls_debug "fail_setup_confirm" <0/1> + mlanutl mlanX tdls_debug "higher_lower_mac" <0/1> + mlanutl mlanX tdls_debug "ignore_key_expiry" <0/1> + mlanutl mlanX tdls_debug "setup_existing_link" <0/1> + mlanutl mlanX tdls_debug "setup_with_prohibited" <0/1> + mlanutl mlanX tdls_debug "stop_rx" <0/1> + mlanutl mlanX tdls_debug "wrong_bss" <0/1> + mlanutl mlanX tdls_disable_cs <0/1> + mlanutl mlanX tdls_discovery <tdls.conf> + mlanutl mlanX tdls_link_status + mlanutl mlanX tdls_powermode <tdls.conf> + mlanutl mlanX tdls_setinfo <tdls.conf> + mlanutl mlanX tdls_setup <tdls.conf> + mlanutl mlanX tdls_stop_channel_switch <tdls.conf> + mlanutl mlanX tdls_teardown <tdls.conf> + mlanutl mlanX thermal + mlanutl mlanX ts_status + mlanutl mlanX tsf + mlanutl mlanX txaggrctrl [m] + mlanutl mlanX txbufcfg + mlanutl mlanX txratecfg [l] [m] [n] + mlanutl uapX txratecfg [l] [m] [n] + mlanutl mlanX verext + mlanutl mlanX version + mlanutl mlanX vhtcfg <j> <k> [l] [m] [n] [o] + mlanutl uapX vhtcfg <j> <k> [l] [m] [n] [o] + mlanutl mlanX wakeupreason + mlanutl uapX wakeupreason + mlanutl mlanX warmreset + mlanutl mlanX wpssession [n] + mlanutl mlanX wmmcfg [n] + mlanutl mlanX wmmparamcfg [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP] + mlanutl mlanX wwscfg [m] + mlanutl mlanX mc_cfg [n] + mlanutl mlanX mc_policy [n] + mlanutl mlanX mc_cfg_ext [c] [s] [u] [m] <a> <b> <d> <e> + mlanutl p2pX cfg_noa [h] [i] [j] [k] [l] + mlanutl p2pX cfg_opp_ps [m] [n] + mlanutl mlanX get_sensor_temp + mlanutl <inteface> indrstcfg <ir_mode> [gpio_pin] + + mlanutl <interface> fwwakeupmethod <method> <GPIO_pin> + + mlanutl uapX ctrldeauth <n> + + mlanutl mlanX/uapX bootsleep <1/0> + +DESCRIPTION + Those commands are used to send additional commands to the Marvell MLAN + card via the Linux device driver. + + The mlanX parameter specifies the network device that is to be used to + perform this command on. It could be mlan0, mlan1 etc. + +11dcfg + This command is used to control 11D. No argument is used to get. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + mlanutl mlan0 11dcfg 1 : Enable 11D + mlanutl mlan0 11dcfg : Get 11D status + +11dclrtbl + This command is used to clear the 11D channel table. + + Usage: + mlanutl mlanX 11dclrtbl + +addbapara + This command can be used to update the default ADDBA parameters. + + where <m> is <timeout> + <timeout> - This is the block ack timeout for ADDBA request. + 0 : Disable (recommended for throughput test) + 1 - 65535 : Block Ack Timeout in TU + + where <n> is <txwinsize> + <txwinsize> - Window size for ADDBA request. (16 is recommended and default value) + + where <o> is <rxwinsize> + <rxwinsize> - Window size for ADDBA response. (48 is recommended and 32 is default value) + (16 is recommended for IWNCOMM AP in WAPI throughput test) + + Current window size limit for Tx as well as Rx is 1023. + + where <p> is <txamsdu> + <txamsdu> - amsdu support for ADDBA request. (1 is default value) + 0: disable amsdu in ADDBA request + 1: enable amsdu in ADDBA request + + where <q> is <rxamsdu> + <rxamsdu> - amsdu support for ADDBA response. (1 is default value) + 0: disable amsdu in ADDBA response + 1: enable amsdu in ADDBA response + + eg: + mlanutl mlanX addbapara - This command will get the current addba params + mlanutl mlanX addbapara 1000 64 8 0 0 - This will change the ADDBA timeout to (1000 * 1024) us, + txwinsize to 64 and rxwinsize to 8 and disable amdsu in ADDBA request/response. + + The default setting is 65535 16 32 1 1. + + In case the ADDBA timeout value is updated then a ADDBA is sent for all streams + to update the timeout value. + + In case txwinsize and/or rxwinsize is updated, the effect could only be seen on + next ADDBA request/response. The current streams will not be affected with this + change. + + In case of txamsdu/rxamsdu is updated, the effect could only be seen on + next ADDBA request/response. The current streams will not be affected with this + change. AMSDU in AMPDU stream will be enabled when AP support this feature + and AMSDU is enabled in aggrpriotbl. + +addbareject + This command is used set/get the addbareject table for all the TIDs. + This command can also be used to enable rejection of ADDBA requests for a given tid. + + where <m0> <m1> ... <m7> + + <mX> - This can be 0/1 for TidX. 1 enables rejection of ADDBA request for TidX and + 0 would accept any ADDBAs for TidX. + + eg: + mlanutl mlanX addbareject - This command will get the current table. + [0 0 0 0 0 0 0 0]. ADDBA would be accepted for all TIDs. This is the default state. + + mlanutl mlanX addbareject 0 0 1 1 0 0 0 0 - This command will accept ADDBA requests for + Tid [0,1,4,5,6,7] and reject ADDBA requests for Tid [2,3] + + mlanutl mlanX addbareject 1 1 1 1 1 1 1 1 - This will enable rejection of ADDBA requests for + all Tids. + + Note:- This command should only be issue in disconnected state. + +addts + Send an ADDTS command to the associated AP. + + Process a given conf file for a specific TSPEC data block. Send the + TSPEC along with any other IEs to the driver/firmware for transmission + in an ADDTS request to the associated AP. + + Return the execution status of the command as well as the ADDTS response + from the AP if any. + + Usage: + mlanutl mlanX addts <filename.conf> <section# of tspec> <timeout(ms)> + +adhocaes + This command is used to set/get the AES key, when the station is in ad-hoc mode. + Note: This command is only available in disconnected state. + + where value can be any 16 byte value. + + Examples: + mlanutl mlan0 adhocaes : Get ad-hoc aes key + mlanutl mlan0 adhocaes "1;12345678901234567890123456789012" + : Set ad-hoc aes key + mlanutl mlan0 adhocaes 2 : Clear ad-hoc aes key + +aggrpriotbl + This command is used set/get the priority table for AMPDU/AMSDU traffic per tid. + This command can also be used to disable AMPDU/AMSDU for a given tid. + In case of AMPDU this priority table will be used to setup block ack (to make + sure the highest priority tid always uses AMPDU as we have limited AMPDU streams) + + where <m0> <n0> <m1> <n1> ... <m7> <n7> + + <mx> - This is priority for Tid0 for AMPDU packet. A priority could be any + values between 0 - 7, 0xff to disable aggregation. + <nx> - This is priority for Tid0 for AMSDU packet. A priority could be any + values between 0 - 7, 0xff to disable aggregation. + + eg: + mlanutl mlanX aggrpriotbl - This command will get the current Priority table for AMPDU and AMSDU. + <2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255>. This is read as + <"Prio for AMPDU for Tid0" "Prio for AMSDU for Tid0" + "Prio for AMPDU for Tid1" "Prio for AMSDU for Tid1" and so on + mlanutl mlanX aggrpriotbl 2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255 - + This will set the priority table for AMPDU and AMSDU + Priority for Tid0/AMPDU = 2, Tid0/AMSDU = 2, Tid1/AMPDU = 0, Tid1/AMSDU = 0 + and so on. Aggregation for Tid6 and Tid7 are disabled. + Here higher the priority number, higher the priority (i.e. 7 + has higher priority than 6). Similarly for AMSDU. + mlanutl mlanX aggrpriotbl 0xff 2 0xff 0 0xff 1 0xff 3 0xff 4 0xff 5 0xff 0xff 0xff 0xff - This will disable + AMPDU for all the TIDs but will still keep AMSDU enabled to Tid0 to Tid5 + + The default setting is 2 255 0 255 1 255 3 255 4 255 5 255 255 255 255 255. + + A delBA should be seen in case a disable happens on a TID for which AMPDU stream + is currently setup. + + Note:- This command should only be issue in disconnected state. + +amsduaggrctrl + This command could be used to enable/disable a feature where firmware gives feedback to driver + regarding the optimal AMSDU buffer size to use with the current rate. Firmware will use the + current rate to decide the buffer size we could transmit. The max buffer size will still be + limited by buffer size provided in txbufcfg. (i.e. if the txbufcfg is 4K, then we could only transmit + 4K/2K AMSDU packets, if the txbufcfg is 8K then we could transmit 8k/4k/2k based on current rate) + + If enabled AMSDU buffer size at various rates will be as follows + + 1. Legacy B/G rate. + No AMSDU aggregation. + + 2. BW20 HT Rate: + When TX rate goes down, + MCS 7, 6, 5, 4: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 3, 2: + a 4K aggregation size (if TX buffer size is 8K/4K) + b 2K aggregation size (if TX buffer size is 2K) + + MCS 1, 0: + a No aggregation + + When TX rate goes up, + MCS 7, 6, 5: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 4, 3: + a 4K aggregation size (if TX buffer size is 8K/4K) + b 2K aggregation size (if TX buffer size is 2K) + + MCS 2, 1, 0: + a No aggregation + + 3. BW40 HT Rate: + When TX rate goes down, + MCS 7, 6, 5, 4, 3, 2, 1: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 0: + a No aggregation + + When TX rate goes up, + MCS 7, 6, 5, 4, 3: + a 8K aggregation size (if TX buffer size is 8K) + b 4K aggregation size (if TX buffer size is 4K) + c 2K aggregation size (if TX buffer size is 2K) + + MCS 2, 1, 0: + a No aggregation + + where <n> is 0/1 (for disable/enable) + + eg: + mlanutl mlan0 amsduaggrctrl 1 - Enable this feature + mlanutl mlan0 amsduaggrctrl 0 - Disable this feature + mlanutl mlan0 amsduaggrctrl - This will get the enable/disable flag + and the current AMSDU buffer size). The AMSDU buffer size returned is only + valid after association as before association there is no rate info. + + Note:- This command to enable/disable could be given anytime (before/after + association). This feature is enabled by default by the driver during + initialization. + +antcfg + This command is used to set/get the mode of Tx/Rx path. + + For chip which support STREAM_2X2 + where value of m is: + Bit 0 -- Tx Path A or Tx/Rx Path A if [n] is not provided + Bit 1 -- Tx Path B or Tx/Rx Path B if [n] is not provided + Bit 0-1 -- Tx Path A+B or Tx/Rx Path A+B if [n] is not provided + where value of n is: + Bit 0 -- Rx Path A + Bit 1 -- Rx Path B + Bit 0-1 -- Rx Path A+B + The Tx path setting (m) is used for both Tx and Rx if Rx path (n) is not provided. + + Examples: + mlanutl mlan0 antcfg : Get Tx and Rx path + mlanutl mlan0 antcfg 3 : Set Tx and Rx path to A+B + mlanutl mlan0 antcfg 1 3 : Set Tx path to A and Rx path to A+B + + For chip which support SAD + where value of m is: + Bit 0 -- Tx/Rx antenna 1 + Bit 1 -- Tx/Rx antenna 2 + ... + 0xFFFF -- Tx/Rx antenna diversity + + where value of n is: + SAD evaluate time interval, only be provided when m = 0xFFFF, default value is 6s(0x1770) + + Examples: + mlanutl mlan0 antcfg : Get Tx/Rx antenna mode + mlanutl mlan0 antcfg 1 : Set Tx/Rx antenna 1 + mlanutl mlan0 antcfg 0xFFFF : Set Tx/Rx antenna diversity + mlanutl mlan0 antcfg 0xFFFF 0x1770 : Set antenna evaluate time interval to 6s + +arpfilter + This command is used to configure the ARP filtering parameters. + + Usage: + mlanutl mlanX arpfilter <arpfilter.conf> + + Where the parameter is: + arpfilter.conf : The configuration file specifying ARP filtering parameters. + + Example: + mlanutl mlan0 arpfilter config/arpfilter.conf + +assocessid + This command is used to assoc essid with asynced mode, + and driver will auto retry if driver auto assoc enabled. + + Usage: + mlanutl mlanX assocessid <"[essid]"> + + Where + <"[essid]"> is the essid which need to be associated with asynced mode. + + Examples: + mlanutl mlan0 assocessid "Marvell Micro AP" : Associate to the ESSID "Marvell Micro AP" + +assocessid_bssid + This command is used to assoc AP by ssid/bssid pair with asynced mode, + and driver will auto retry if driver auto assoc enabled. + + Usage: + mlanutl mlanX assocessid_bssid <"[bssid] [essid]"> + + Where + <"[bssid]"> is the bssid which need to be associated with asynced mode. + <"[essid]"> is the essid which need to be associated with asynced mode. + + Examples: + mlanutl mlan0 assocessid_bssid "xx:xx:xx:xx:xx:xx Marvell Micro AP" : Associate to the AP which ssid = "Marvell Micro AP", bssid = "xx:xx:xx:xx:xx:xx" + +associate + Request an association to a given SSID/BSSID pair. This the only accurate + way to pick a specific AP and ESS for an association. The entry must + already exist in the scan table for the association to be attempted. + + mlanutl mlanX associate "xx:xx:xx:xx:xx:xx SSID" + +authtype + This command is used to set/get authentication type. + + Usage: + mlanutl mlanX authtype [n] + + where <n> + 0: 802.11 open system authentication + 1: 802.11 shared key authentication + 255: allow open system or shared key authentication (default) + + Examples: + mlanutl mlan0 authtype 0 : use open system authentication + mlanutl mlan0 authtype 1 : use shared key authentication + mlanutl mlan0 authtype 255 : allow open system or shared key authentication + mlanutl mlan0 authtype : get current setting + +autotdls + This command is used to enable/disable auto TDLS. + + Usage: + mlanutl mlanX autotdls [n] + + where <n> + 0: Disable auto tdls + 1: Enable auto tdls + + Examples: + mlanutl mlan0 autotdls 1 : enable auto TDLS + mlanutl mlan0 autotdls 0 : disable auto TDLS + mlanutl mlan0 autotdls : get current setting + +dyn_bw + This command is used to set/get dynamic bandwidth. + + Usage: + mlanutl mlanX dyn_bw [n] + + where <n> + [BIT0] + 0 = TxInfo Indicated BW Disable + 1 = TxInfo Indicated BW Enable + [BIT1] + 0 = TxInfo Dynamatic BW Disable + 1 = TxInfo Dynamatic BW Enable + [BIT2] + 0 = TxInfo Force send RTS Disable + 1 = TxInfo Force send RTS Enable + [BIT3] + 0 = Mac Dynamic BW Operation Mode Disable (Static BW Operation Mode) + 1 = Mac Dynamic BW Operation Mode Enable + other bits reserved. + + If no parameter provided, get is performed. + + Examples: + mlanutl mlan0 dyn_bw 0x1 : Enable TxInfo Indicated BW + mlanutl mlan0 dyn_bw : get current setting + +tdls_idle_time + This command is used to set/get TDLS idle timeout. The valid value is between 0-0xffff. When set to 0, the tdls_idle_time will use default value(60). + + Usage: + mlanutl mlanX tdls_idle_time [n] + + where <n> + TDLS idle timeout value + + Examples: + mlanutl mlan0 tdls_idle_time 30 : set tdls_idle_time value to 30 + mlanutl mlan0 tdls_idle_time 0 : use default tdls_idle_time value(60) + mlanutl mlan0 tdls_idle_time : get current setting + +dfs_offload + This command is used to enable/disable DFS offload. The valid value is 0/1. + Note: The parameters can be set only in disconnected state. + + Usage: + mlanutl uapX dfs_offload [n] + + where <n> + Enable/disable + + Examples: + mlanutl uap0 dfs_offload 1 : enable DFS offload + mlanutl uap0 dfs_offload 0 : disable DFS offload + +bandcfg + This command is used to set/get infra/ad-hoc band. + Note: This command is only available in disconnected state. + + Usage: + mlanutl mlanX bandcfg [l] [m] [n] [o] + + where the parameters: + [l]: Infrastructure band + bit 0: B + bit 1: G + bit 2: A + bit 3: GN + bit 4: AN + + bit 5: AC 2.4G + bit 6: AC 5G + [m]: Ad-hoc start band + bit 0: B + bit 1: G + bit 2: A + bit 3: GN + bit 4: AN + bit 5: AC 2.4G + bit 6: AC 5G + [n]: Ad-hoc start channel + [o]: 0 - Bandwidth 20Mhz + 1 - HT Bandwidth 40Mhz above + 3 - HT Bandwidth 40Mhz below + 4 - VHT Bandwidth 80Mhz + Examples: + mlanutl mlan0 bandcfg : Get infra/ad-hoc band and ad-hoc + start channel configurations + mlanutl mlan0 bandcfg 1 : Set infra band to B only + mlanutl mlan0 bandcfg 3 2 6 : Set infra band to B/G, ad-hoc start band + to G and ad-hoc start channel to 6 + mlanutl mlan0 bandcfg 7 11 6 1 : Set infra band to B/G/A, ad-hoc start band + to B/G/GN, ad-hoc start channel to 6 and + secondary channel to above + +bcninterval + This command is used to set/get the beacon interval in ad-hoc mode. + The valid beacon interval is between 20 - 1000, default beacon + interval is 100. + + Where <n> + Beacon interval in TU (Time Unit: 1024 us). + + Examples: + mlanutl mlan0 bcninterval 200 : Set ad-hoc beacon interval to 200 + mlanutl mlan0 bcninterval : Get ad-hoc beacon interval + +bssrole + This command is used to set/get the BSS role. + + Where + [l] is <bss_role> + <bss_role> - This parameter specifies the BSS role to set. + 0 : STA + 1 : uAP + + Examples: + mlanutl wfd0 bssrole : Get the current BSS role + mlanutl wfd0 bssrole 1 : Set the current BSS role to uAP + +cfgdata + This command is used to set/get the configuration data to/from firmware. + + Usage: + mlanutl mlanX cfgdata <type> [<.conf file name>] + + Where the parameters are: + type : + 2 -- CAL data download and <.conf file name> is cal_data.conf + .conf file name : The configuration file used to set/get the configuration data. + + Examples: + mlanutl mlan0 cfgdata 2 + : This command is used to get and display the CAL data from firmware. + +cfpcode + This command is used to set/get the Channel-Frequency-Power table codes. + The region table can be selected through region code. + The current configuration is returned if no parameter provided. + + where the parameters are, + [m]: code of the CFP table for 2.4GHz (0: unchanged) + [n]: code of the CFP table for 5GHz (0 or not provided: unchanged) + + Examples: + mlanutl mlan0 cfpcode : Get current configuration + mlanutl mlan0 cfpcode 0x30 : Set 2.4GHz CFP table code 0x30 (EU), + keep 5GHz table unchanged + mlanutl mlan0 cfpcode 0x10 5 : Set 2.4GHz CFP table code 0x10 (USA) + and 5GHz table code 5 + +changraph + Displays 2-dimensional graph, plotting channel number along x-axis and + anpi or channel-load along y-axis, depending on whether it is an anpi graph + or a channel load graph. + + Usage: + mlanutl mlanX changraph [<load | anpi | anpiload> <loops>] + where: + load: Only channel vs channel-load graph is displayed + anpi: Only channel vs Average Noise Power Indicator(ANPI) + graph is displayed + anpiload: Both the graphs for anpi and for the load are displayed + loops: This is used to calculate the number of times + the graph [load or anpi or both] will be printed + +coex_rx_winsize + This command is used to set/get control to coex RX window size + + where value of m is: + 0 -- Disable COEX RX winsize (default) + 1 -- Enable COEX RX winsize + + Examples: + mlanutl mlan0 coex_rx_winsize : Get COEX RX winsize + mlanutl mlan0 coex_rx_winsize 1 : Enable COEX RX winsize + +countrycode + This command is used to set and get the country code. + + Where + [l] is Country code + + Examples: + mlanutl mlan0 countrycode : Get current countrycode + mlanutl mlan0 countrycode CN : Set countrycode as China (CN) + +cfpinfo + This command is used to get region, country, environment codes, + channel and power table information from the FW. + + Examples: + mlanutl mlan0 cfpinfo : Display cfp tables + mlanutl uap0 cfpinfo + +acs + This command is used to issue acs scan, then FW will + report the best channel and channel statistics. + User could specify channels or not. If channel is not + specified, acs scan will be on all supported channels. + + Examples: + mlanutl mlan0 acs : ACS scan on all supported channels + mlanutl uap0 acs : ACS scan on all supported channels + mlanutl mlan0 acs 5 6 7 8 : ACS scan on 5 6 7 8 channels + mlanutl uap0 acs 1 2 3 : ACS scan on 1 2 3 channels + +customie + This command is used to set or get custom IEs for management frames. + + Usage: + mlanutl mlanX customie [[[<index>] <mask>] <IEBuffer>] + + Where the parameter is: + empty - Get all IE settings + <index> : 0 - Get/Set IE index 0 setting + 1 - Get/Set IE index 1 setting + 2 - Get/Set IE index 2 setting + MAX IE Index depends on device memory. + -1 - Append/Delete IE automatically + Delete will delete the IE from the matching IE buffer + Append will append the IE to the buffer with the same mask + <mask> : Management subtype mask value as per bit definitions + : Bit 0 - Association request + : Bit 1 - Association response + : Bit 2 - Reassociation request + : Bit 3 - Reassociation response + : Bit 4 - Probe request + : Bit 5 - Probe response + : Bit 8 - Beacon + <mask> : mask = 0 to clear the mask and the IE buffer + <IEBuffer> : IE Buffer in hex (max 256 bytes) + The Buffer should not be space separated. + + Examples: + mlanutl mlan0 customie + : Get IE buffer, subtype mask settings for all indices. + + mlanutl mlan0 customie 1 + : Get IE buffer and subtype mask for the Index = 1. + + mlanutl mlan0 customie 2 0 + : Clear IE buffer and mask value for Index = 2. + + mlanutl mlan0 customie 3 0x101 0xdd051234567890 + : Set IE buffer and mask value for Index = 3. + + mlanutl mlan0 customie -1 0x101 0xdd051234567890 + : Append the specified IEBuffer at index with mask value of 0x101. + + mlanutl mlan0 customie -1 0 0xdd051234567890 + : Delete the specified IEBuffer from all the IEs. + + mlanutl mlan0 customie 2 0 0xdd051234567890 + : Delete the specified IEBuffer from the IEs at index 2. + +deauth + This command is used to send a de-authentication to an arbitrary AP. + If [l] is omitted, the driver will deauth the associated AP. + If in ad-hoc mode this command is used to stop beacon transmission + from the station and go into idle state. + + When <l> is supplied as a MAC address, the driver will deauth the + specified AP. If the AP address matches the driver's associated AP, + the driver will disconnect. Otherwise, the driver remains connected. + + When this command is executed on AP interface, it is used to send + a de-authentication to associated station. + +deepsleep + This command is used to set/get auto deep sleep mode. + + Usage: + mlanutl mlanX deepsleep [l] [m] + + where the parameters are: + [l]: Enable/disable auto deep sleep mode (1/0) + [m]: Idle time in milliseconds after which firmware will put the device + in deep sleep mode. Default value is 100 ms. + + Examples: + mlanutl mlan0 deepsleep : Display auto deep sleep mode + mlanutl mlan0 deepsleep 1 : Enable auto deep sleep mode, idle time unchanged + mlanutl mlan0 deepsleep 0 : Disable auto deep sleep mode + mlanutl mlan0 deepsleep 1 500 : Enable auto deep sleep mode with idle time 500 ms + Note: + Deepsleep must be disabled before changing idle time. + +delba + This command is used to delete either all Tx BA or all Rx BA or a specific BA stream + based on direction, TID and peer address. + + where <l> [<m> <n>] + <l> - This is the direction of BA stream, Tx (bit 0), Rx (bit 1). + <m> - This is the TID (0-7, 0xff for all) of BA stream. + <n> - This is the peer MAC addres of BA stream. + + eg: + mlanutl mlanX delba 2 - This command will delete all the Rx BA streams. + mlanutl mlanX delba 3 - This command will delete all the Tx and Rx BA streams. + mlanutl mlanX delba 1 0 - This command will delete all the Tx streams with TID 0. + mlanutl mlanX delba 2 0xff "00:11:22:33:44:55" - This command will delete all the Rx BA streams + with specified peer MAC address + mlanutl mlanX delba 1 3 "00:11:22:33:44:55" - This command will delete the Tx BA stream with + TID 3 and specified peer MAC address. + +delts + Send a DELTS command to the associated AP. + + Process a given conf file for a specific TSPEC data block. Send the + TSPEC along with any other IEs to the driver/firmware for transmission + in a DELTS request to the associated AP. + + Return the execution status of the command. There is no response to a + DELTS from the AP. + + Usage: + mlanutl mlanX delts <filename.conf> <section# of tspec> + +dfstesting + This command is used to set/get settings for DFS testing. + + Usage: + mlanutl mlanX dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>] + + where <user_cac_pd> is user-configured Channel Availability Check in msec + 0 = disable, use default period (60000) + 1-65535 = enable with that period + where <user_nop_pd> is user-configured Non-Occupancy Period in sec + 0 = disable, use default period (1800) + 1-65535 = enable with that period + where <no_chan_change> is enable/disable no channel change on radar + 0 = disable, 1 = enable (overrides below) + where <fixed_chan_num> is user-configured channel to change to on radar + 0 = disable, 1-255 = enable with that channel + (channel validity for region, etc. is not checked) + (only takes effect if no_chan_change = 0) + + Examples: + mlanutl mlan0 dfstesting : Get current dfstesting settings + mlanutl mlan0 dfstesting 2000 0 0 0 : user_cac=2sec, others disabled/default + mlanutl mlan0 dfstesting 0 0 1 0 : only no_chan_change enabled + mlanutl mlan0 dfstesting 0 120 0 64 : user_nop=2min, force chan 64 on radar + +drvdbg + This command is used to set/get the bit masks of driver debug message control. + + Usage: + mlanutl mlanX drvdbg [n] + + Where the parameter <n> is the generic debug message control bit mask. + The following types of driver debug messages can be dynamically enabled or + disabled by setting or clearing the corresponding bits, + bit 0: MMSG PRINTM(MMSG,...) + bit 1: MFATAL PRINTM(MFATAL,...) + bit 2: MERROR PRINTM(MERROR,...) + bit 3: MDATA PRINTM(MDATA,...) + bit 4: MCMND PRINTM(MCMND,...) + bit 5: MEVENT PRINTM(MEVENT,...) + bit 6: MINTR PRINTM(MINTR,...) + bit 7: MIOCTL PRINTM(MIOCTL,...) + ... + bit 16: MDAT_D PRINTM(MDAT_D,...), DBG_HEXDUMP(MDAT_D,...) + bit 17: MCMD_D PRINTM(MCMD_D,...), DBG_HEXDUMP(MCMD_D,...) + bit 18: MEVT_D PRINTM(MEVT_D,...), DBG_HEXDUMP(MEVT_D,...) + bit 19: MFW_D PRINTM(MFW_D,...), DBG_HEXDUMP(MFW_D,...) + bit 20: MIF_D PRINTM(MIF_D,...), DBG_HEXDUMP(MIF_D,...) + ... + bit 28: MENTRY PRINTM(MENTRY,...), ENTER(), LEAVE() + bit 29: MWARN PRINTM(MWARN,...) + bit 30: MINFO PRINTM(MINFO,...) + + If CONFIG_DEBUG=2, all kinds of debug messages can be configured. + + If CONFIG_DEBUG=1, all kinds of debug messages can be configured except + for MENTRY, MWARN and MINFO. By default MMSG, MFATAL and MERROR are enabled. + + Some special debug messages, + '*' // MLAN driver ISR is called (bit 6 MINTR enabled) + '|' // PS awake event is received (bit 5 MEVENT enabled) + '_' // PS sleep event is received (bit 5 MEVENT enabled) + '+' // PS sleep confirm is sent (bit 5 MEVENT enabled) + + Examples: + mlanutl mlan0 drvdbg : Get the current driver debug masks + mlanutl mlan0 drvdbg 0 : Disable all the debug messages + mlanutl mlan0 drvdbg 7 : Enable MMSG, MFATAL and MERROR messages + mlanutl mlan0 drvdbg 0x20037 : Enable MMSG, MFATAL, MEEROR, + MCMND, MEVENT and MCMD_D messages + mlanutl mlan0 drvdbg -1 : Enable all the debug messages + +esuppmode + This command is used to set/get the e-supplicant mode configurations/status. + + Note: The configurations can be set only before association. + For get, the configurations will be returned before association + and the current status will be returned after association. + + Where + [l] is <rsn_mode> + <rsn_mode> - This parameter specifies the RSN mode configuration + Bit 0 : No RSN + Bit 1-2 : RFU + Bit 3 : WPA + Bit 4 : WPA-NONE + Bit 5 : WPA2 + Bit 6-15 : RFU + [m] is <pairwise_cipher> + <pairwise_cipher> - This parameter specifies the pairwise cipher + Bit 0 : RFU + Bit 1 : RFU + Bit 2 : TKIP + Bit 3 : AES + Bit 4-7 : RFU + [n] is <group_cipher> + <group_cipher> - This parameter specifies the group cipher + Bit 0 : RFU + Bit 1 : RFU + Bit 2 : TKIP + Bit 3 : AES + Bit 4-7 : RFU + Note that: the RFU bits cannot be SET. + + Examples: + mlanutl mlan0 esuppmode : Get RSN mode and pairwise/group cipher + mlanutl mlan0 esuppmode 8 4 4 : Set RSN mode yo WPA, active pairwise and + group ciphers to TKIP + +extcapcfg + This command is used to set/get extended capabilities. + + Usage: + mlanutl mlanX extcapcfg [<ext_cap>] + + where <ext_cap> : Extended capabilities in hex (max 9 bytes) + The Buffer should not be space separated. + + Examples: + mlanutl mlan0 extcapcfg 0x0000008020 : Set TDLS support and Interworking bits. + +fwmacaddr + This command is used to set/get FW side MAC address but host side address will remain as earlier. + + Usage: + mlanutl mlanX fwmacaddr [mac_addr] + + where <mac_addr> is desired MAC address + + Examples: + mlanutl mlan0 fwmacaddr : Get current FW MAC address + mlanutl mlan0 fwmacaddr 00:50:43:20:bc:44 : Set FW side MAC address + +getdatarate + This command is used to get the data rate being used in last Tx + packet and last Rx packet. + +getkey + This command is used to get PTK/GTK + + mlanutl mlanX getkey + +getlog + This command is used to get the statistics available in the station. + Following stats are displayed:- + dot11MulticastTransmittedFrameCount Increments when the multicast bit is set in the destination + MAC address of a successfully transmitted MSDU + + dot11FailedCount Increments when an MSDU is not transmitted successfully + + dot11RetryCount Increments when an MSDU is successfully transmitted after one + or more retransmissions + + dot11MultipleRetryCount Increments when an MSDU is successfully transmitted after more + than one retransmission + + dot11FrameDuplicateCount Increments when a frame is received that the Sequence Control + field is indicating a duplicate count + + dot11RTSSuccessCount Increments when a CTS is received in response to an RTS + + dot11RTSFailureCount Increments when a CTS is not received in response to an RTS + + dot11ACKFaliureCount Increments when an ACK is not received when expected + + dot11ReceivedFragmentCount Increments for each successfully received MPDU of type Data or Management + + dot11MulticastReceivedFrameCount Increments when a MSDU is received with the multicast bit set in the destination MAC address + + dot11FCSErrorCount Increments when an FCS error is detected in a received MPDU + + dot11TransmittedFrameCount Increments for each successfully transmitted MSDU + + dot11WeplcvErrCnt Increment when WEP decryption error for key index 0.3 + + beaconReceivedCnt Increments when received beacon + + beaconMissedCnt Increments when beacon missed + + dot11TransmittedFrameCount Increments for each successfully transmitted MSDU + + dot11QosTransmittedFragmentCount Increments when a corresponding UP's MPDU transmitted successfully + + dot11QosFailedCount Increments when a corresponding UP's MSDU not transmitted successfully + + dot11QosRetryCount Increment when a corresponding UP's MSDU transmitted successfully after one or more retransmission + + dot11QosMultipleRetryCount Increments when a corresponding UP's MSDU is successfully transmitted after more than one retransmission + + dot11QosFrameDuplicateCount Increments when a corresponding UP's frame is received that the Sequence + Control field is indicating a duplicate frame + + dot11QosRTSSuccessCount Increments when a CTS is received in response to an RTS, which is sent for a corresponding UP's Qos frame + + dot11QosRTSFailureCount Increments when a CTS is not received in response to an RTS, which is sent for a corresponding UP's + Qos frame + + dot11QosACKFailureCount Increments when an ACK is not received when expected for a corresponding UP's Qos frame + + dot11QosReceivedFragmentCount Increments when a corresponding UP's MPDU received + + dot11QosTransmittedFrameCount Increments when a corresponding UP's MSDU transmitted + + dot11QosDiscardedFrameCount Increments when a corresponding UP's MSDU not transmitted successfully + + dot11QosMPDUsReceivedCount Increments when a corresponding UP's MDPU received + + + dot11QosRetriesReceivedCount Increments when a corresponding UP's MDPU received which retry bit is set + + dot11RSNAStatsCMACICVErrors Increment when a MPDU discard by CMAC integrity check + + dot11RSNAStatsCMACReplays Increments when a MPDU discarded by the CMAC replay error + + dot11RSNAStatsRobustMgmtCCMPReplays Increments when a robust management frame discarded by CCMP replay error + + dot11RSNAStatsTKIPICVErrors Increments when a MPDU discarded by TKIP ICV error + + dot11RSNAStatsTKIPReplays Increments when a MPDU discarded by TKIP replay error + + dot11RSNAStatsCCMPDecryptErrors Increments when a MPDU discarded by CCMP decryption error + + dot11RSNAStatsCCMPReplays Increments when a MPDU discarded by CCMP replay error + + dot11TransmittedAMSDUCount Increments when a A-MSDU transmitted successfully + + dot11FailedAMSDUCount Increments when a A-MSDU not transmitted successfully + + dot11RetryAMSDUCount Increments when a A-MSDU is successfully transmitted after one or more retransmissions + + dot11MultipleRetryAMSDUCount Increments when a A-MSDU is successfully transmitted after more than one retransmissions + + dot11TransmittedOctetsInAMSDUCount Increments by the number of octets in the frame body + of an A-MSDU frame when an A-MSDU frame is successfully transmitted + + dot11AMSDUAckFailureCount Increments when an acknowledgment to an A-MSDU is not received when expected. + + dot11ReceivedAMSDUCount Increments when a A-MSDU frame received + + dot11ReceivedOctetsInAMSDUCount Increments by the number of octets in the frame body + of an A-MSDU frame when an A-MSDU frame is received + + dot11TransmittedAMPDUCount Increments when an A-MPDU is transmitted + + dot11TransmittedMPDUsInAMPDUCount Increments by the number of MPDUs in the A-MPDU when an A-MPDU is transmitted + + dot11TransmittedOctetsInAMPDUCount Increments by the number of octets in the A-MPDU frame when an A-MPDU frame is transmitted + + dot11AMPDUReceivedCount Increments when the MAC receives an A-MPDU from the PHY + + dot11MPDUInReceivedAMPDUCount Increments by the number of MPDUs received in the + A-MPDU when an A-MPDU is received + + dot11ReceivedOctetsInAMPDUCount Increments by the number of octets in the A-MPDU + frame when an A-MPDU frame is received + + dot11AMPDUDelimiterCRCErrorCount Increments when an MPDU delimiter has a CRC error when this is the first + CRC error in the received A-MPDU or when the previous delimiter has been decoded correctly + +getscantable + Display the current contents of the driver scan table + + Usage: + mlanutl mlanX getscantable + mlanutl mlanX getscantable [#] + mlanutl mlanX getscantable tsf + mlanutl mlanX getscantable ch + mlanutl mlanX getscantable help + + 1) Without argument, the entire scantable is displayed in terms of channel (ch), signal strength (ss), BSS id (bssid), capability (cap), and SSID, + where each column in the capability is described as follows: + column 1 indicates the IBSS capability: A (Adhoc), I (Infra) + column 2 indicates the encryption capability: P (WEP), W (WPA), 2 (WPA2) + column 3 indicates the 11D capability: D (11D) + column 4 indicates the WMM capability: W (WMM), C (CAC) + column 5 indicates the 11K capability: K (11K) + column 6 indicates the 11R capability: R (11R) + column 7 indicates the WPS capability: S (WPS) + column 8 indicates the 11N/11AC capability: N (11N), A (11AC) + + 2) Specifying a # will display detailed information about a specific scan + table entry. '0' displays driver cached information regarding the + current association (if any). + 3) The tsf argument will display the entire scan table with the recorded + TSF timestamp for the entry. + 4) The ch argument will display the entire scan table sorted by channel + number in the ascending order. If this argument is not specified, + scan table is sorted by signal strength in the descending order. + 6) The help argument will display the legend for the capability field. + +getsignal + This command gets the last and average value of RSSI, SNR and NF of + Beacon and Data. + Note: This command is available only when STA is connected. + + where value of m is: + 1 -- RSSI (Receive Signal Strength Indication) + 2 -- SNR (Signal to Noise Ratio) + 3 -- NF (Noise Floor) + where value of n is: + 1 -- Beacon last + 2 -- Beacon average + 3 -- Data last + 4 -- Data average + + Examples: + mlanutl mlan0 getsignal 1 : Get the RSSI info (beacon last, beacon + average, data last and data average) + mlanutl mlan0 getsignal 3 4 : Get the NF of data average + mlanutl mlan0 getsignal 2 1 : Get the SNR of beacon last + mlanutl mlan0 getsignal : Get all of the signal info + mlan0 getsignal:-32 -33 -35 -36 67 59 63 56 -99 -92 -98 -92 + RSSI info: beacon last -32, beacon average -33, data last -35, data average -36 + SNR info: beacon last 67, beacon average 59, data last 63, data average 56 + NF info: beacon last -99, beacon average -92, data last -98, data average -92 + +getsignalext + This command gets the last and average value of RSSI, SNR and NF of + Beacon and Data of spectific antenna path. + Note: This command is available only when STA is connected. + + where value of m is: + 1 -- PATH A + 2 -- PATH B + 3 -- PATH A+B + + Examples: + mlanutl mlan0 getsignalext :Get All path's signal. + mlanutl maln0 getsignalext 3 :Get path A+B signal + mlanutl maln0 getsignalext 1 :Get path A signal + mlanutl mlan0 getsignalext 2 :Get path B signal + + PATH A: -46 -46 -49 -49 65 45 39 42 -111 -91 -88 -91 + + PATH A: RSSI info: beacon last -46, beacon average -46, data last -49, data average -49 + SNR info: beacon last 65, beacon average 45, data last 39, data average 42 + NF info: beacon last -111, beacon average -91, data last -88, data average -91 + +signalextcfg + This command is used to enable/disable signalext + Usage: + mlanutl mlanX signalextcfg [m] + where the value of [m] is: + 1 -- enable signalext in firmware + 0 -- disable signalext in firmware + Examples: + mlanutl mlan0 signalextcfg 1 : Enable signalext in firmware + mlanutl mlan0 signalextcfg 0 : Disable signalext in firmware + +getsignalextv2 + This command gets the last and average value of RSSI, SNR and NF of + Beacon and Data of spectific antenna path. + Note: This command is available only when STA is connected. + + where value of m is: + 1 -- PATH A + 2 -- PATH B + 3 -- PATH A+B + + Examples: + mlanutl mlan0 getsignalext :Get All path's signal. + mlanutl maln0 getsignalext 3 :Get path A+B signal + mlanutl maln0 getsignalext 1 :Get path A signal + mlanutl mlan0 getsignalext 2 :Get path B signal + + PATH A: -46 -46 -49 -49 65 45 39 42 -111 -91 -88 -91 + + PATH A: RSSI info: beacon last -46, beacon average -46, data last -49, data average -49 + SNR info: beacon last 65, beacon average 45, data last 39, data average 42 + NF info: beacon last -111, beacon average -91, data last -88, data average -91 + +getstalist + This command is used to get list of associated stations to the AP. + + Example: + mlanutl uap0 getstalist + +channel_switch <switch mode> <oper class> <new channel> <switch count> <bandwidth> + This command is used to do channel switch according to spec. + + Where the paramters are: + switch mode : 0 -- no need to block traffic, 1 -- need block traffic + oper class : operating class according to IEEE std802.11 spec, when 0 is used, only CSA IE will be used + new channel : the channel will switch to + switch count: channel switch time to send ECSA ie + bandwidth : channel width switch to(optional),only for 5G channels. + Support value 1 -- 40M above, 3 -- 40M below, 4 -- 80M, 5 -- 160M + + Example: + mlanutl uap0 channel_switch 1 115 36 10 :switch to channel 36, oper class 115 + mlanutl uap0 channel_switch 1 81 6 10 :switch to channel 6, oper class 81 + mlanutl uap0 channel_switch 1 0 6 10 :switch to channel 6 + mlanutl uap0 channel_switch 1 0 36 10 1 :switch to channel 36, bandwidth 40MHz above + +hostcmd 2040coex + This command is used to send the 11n 20/40 Coex command to firmware. + Firmware will send 11n 20/40 Coex management action frame to AP. + + Usage: + mlanutl mlanX hostcmd config/11n_2040coex.conf 2040coex + +hostcmd auto_tx_get +hostcmd auto_tx_unreg + This command is used to configures the Frame Auto Transmission parameters. + auto_tx_get: get auto_tx parameters + auto_tx_unreg: unregister to firmware auto_tx + + Usage: + mlanutl mlanX hostcmd config/auto_tx.conf auto_tx_get + mlanutl mlanX hostcmd config/auto_tx.conf auto_tx_unreg + +hostcmd bgscfg + This command is used to configure the various parameters for PPS/UAPSD + or normal background scan. + + Usage: + mlanutl mlanX hostcmd config/bg_scan.conf bgscfg + +hostcmd <pkt_coalescing.conf> coalesce_cfg + This command is used to set/clear rules to filter and buffer + broadcast/multicast packet which reduce unwanted patcket or interrupt to + host. + + Usage: + mlanutl mlanX hostcmd <pkt_coalescing.conf> coalesce_cfg + +hostcmd <ed_mac_ctrl.conf> ed_mac_ctrl + This command is used to control ED MAC. + + Usage: + mlanutl mlanX hostcmd <ed_mac_ctrl.conf> ed_mac_ctrl + +hostcmd crypto_test + This command is used to test the encryption/decryption API of the firmware. + + Usage: + mlanutl mlanX hostcmd config/crypto_test.conf crypto_test + +hostcmd nat_keep_alive + This command is used to configures the Frame Auto Transmission parameters. + nat_keep_alive: register to firmware for sending NAT Keep Alive packet + + Usage: + mlanutl mlanX hostcmd config/auto_tx.conf nat_keep_alive + +hostcmd pad_cfg_get +hostcmd pad_cfg_set + This command is used to set/get the configuration data for PAD OR. + + Usage: + mlanutl mlanX hostcmd config/pad_cfg.conf pad_cfg_get + mlanutl mlanX hostcmd config/pad_cfg.conf pad_cfg_set + +hostcmd requesttpc + This command is used to request 802.11H TPC info. + + Usage: + mlanutl mlanX hostcmd config/requesttpc.conf requesttpc + +hostcmd mode_get +hostcmd mode_timeshare +hostcmd mode_spatial +hostcmd mode_none + This command is used to get/set Robust BT Coex. + mode_get: get the current mode + mode_timeshare: set Robust BT Coex to timeshare mode (default on 1x1 chips) + mode_spatial: set Robust BT Coex to spatial mode (only for, and default on 2x2 chips) + mode_none: set Robust BT Coex to mode none (only for, and default on 2x2_3Antenna chips) + + Usage: + mlanutl mlanX hostcmd config/robust_btc.conf mode_get + mlanutl mlanX hostcmd config/robust_btc.conf mode_timeshare + mlanutl mlanX hostcmd config/robust_btc.conf mode_spatial + mlanutl mlanX hostcmd config/robust_btc.conf mode_none + +hostcmd gpio_cfg + This command is used to enable/disable GPIO cfg. + gpio_cfg: enable/disable GPIO cfg for external bt request (default is enable with High Polarity) + + Usage: + mlanutl mlanX hostcmd config/robust_btc.conf gpio_cfg + +hostcmd generictime +hostcmd a2dptime +hostcmd inquirytime +hostcmd ap_generictime +hostcmd ap_a2dptime +hostcmd ap_inquirytime + This command is used to configure the time slice of COEX (only works in timeshare mode) + generictime: configure the Bttime and Wlantime in Station Generic case + a2dptime: configure the Bttime and Wlantime in Station A2DP case + inquirytime: configure the Bttime and Wlantime in Station Inquiry case + ap_generictime: configure the Bttime and Wlantime in Ap Generic case + ap_a2dptime: configure the Bttime and Wlantime in Ap A2DP case + ap_inquirytime: configure the Bttime and Wlantime in Ap Inquiry case + + Usage: + mlanutl mlanX hostcmd config/robust_btc.conf generictime + mlanutl mlanX hostcmd config/robust_btc.conf a2dptime + mlanutl mlanX hostcmd config/robust_btc.conf inquirytim + mlanutl mlanX hostcmd config/robust_btc.conf ap_generictime + mlanutl mlanX hostcmd config/robust_btc.conf ap_a2dptime + mlanutl mlanX hostcmd config/robust_btc.conf ap_inquirytime + +hostcmd sdio_pulldown_get +hostcmd sdio_pulldown_set +hostcmd sdio_pulldown_disable + This command is used to set/get the settings of pulling up and + pulling down of SDIO lines. + + Usage: + mlanutl mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_get + mlanutl mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_set + mlanutl mlanX hostcmd config/sdio_pulldown.conf sdio_pulldown_disable + +hostcmd subevent_get +hostcmd subevent_set + This command is used to get/set the configurations for event descriptor + interface command. + subsvent_get: get subscribed event parameters + subsvent_set: set subscribed event parameters + + Usage: + mlanutl mlanX hostcmd config/subevent.conf subevent_get + mlanutl mlanX hostcmd config/subevent.conf subevent_set + +hostcmd txpwrlimit_2g_cfg_set +hostcmd txpwrlimit_5g_cfg_set +hostcmd txpwrlimit_cfg_get + This command is used to set/get the configuration data of Tx power limitation. + Note: The configuration set should be issued when STA is disconnected. + + Usage: + mlanutl mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_cfg_get + mlanutl mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_2g_cfg_set + mlanutl mlanX hostcmd config/txpwrlimit_cfg.conf txpwrlimit_5g_cfg_set + +hostcmd txrate_cfg_get +hostcmd txrate_cfg_set_bg +hostcmd txrate_cfg_set_bgn + This command is used to set/get the transmit data rate. + + Usage: + mlanutl mlanX hostcmd config/txrate_cfg.conf txrate_cfg_get + mlanutl mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bg + mlanutl mlanX hostcmd config/txrate_cfg.conf txrate_cfg_set_bgn + +hostcmd generate_raw + This command is used to generate the raw data(hostcommand block) for + hostcommand in <conf_file> and write that to file <raw_data_file> + + Usage: + mlanutl mlanX hostcmd <conf_file> generate_raw <raw_data_file> + +hostcmd fwdump + This command is used to trigger firmware dump + + Usage: + mlanutl mlanX hostcmd <fwdump.conf> fwdump + +hotspotcfg + This command is used to get/set the HotSpot configuration. + + Usage: + mlanutl mlanX hotspotcfg [<bitmap>] + + Where the parameter is: + <bitmap> : configuration bitset + : Bit 31-10 - Reserved set to 0 + : Bit 9 - TDLS support indication enable/disable + : Bit 8 - Interworking indication enable/disable + : Bit 7-1 - Reserved set to 0 + : Bit 0 - HotSpot feature enable/disable + + Examples: + mlanutl mlan0 hotspotcfg : Get present remote address mode + mlanutl mlan0 hotspotcfg 0x301 : Turn on HotSpot2.0 and enable TDLS support and interworking indication + mlanutl mlan0 hotspotcfg 0 : Turn off HotSpot2.0 and disable TDLS support and interworking indication + +hscfg + This command is used to configure the host sleep parameters. + + Usage: + mlanutl mlanX hscfg [condition [[GPIO# [gap]]]] (optional)[type ind_GPIO# [level]] (optional)[type event_force_ignore event_use_ext_gap ext_gap [gpio_wave]] + + This command takes one (condition), two (condition and GPIO#) or three + (condition, GPIO# and gap). If more than three parameters, it can set different or multiple features indicating by type(this is optional): + + If type=1, it will set indication gpio and its level. And the parameter format will be (condition, GPIO#,gap and type,ind_GPIO#) or + (condition, GPIO#, gap, type, ind_GPIO# and level). + + If type=2, it will set extend hscfg wakup method. And the parameter format will be (condition, GPIO#, gap, type, force_ignore, + use_ext_gap, ext_gap [gpio_wave]). gpio_wave parameter is optional and default value is 0(falling edge). Each bit of + event_force_ignore and event_use_ext_gap will be defined to one same event, and set one same event(same bit) in those two + parameters is not allowed. Set bit(s) in event_force_ignore means the event(s) will be forced ignore in firmware silently. + Set bit(s) in event_use_ext_gap mean the event(s) will use extend gap to inform host. Not set means not handle. + + If type=3, it will set hs_wakeup_interval. + + If no parameter provided, get is performed. + + The usages of parameters for "hscfg" are the same as that for "hssetpara" command. + +hssetpara + This command is used to set host sleep parameters. + + Usage: + mlanutl mlanX hssetpara condition [GPIO# [gap]] (optional)[type ind_GPIO# [level]] (optional)[type event_force_ignore event_use_ext_gap ext_gap [gpio_wave]] (optional)[type hs_wakeup_interval] + + This command takes one (condition), two (condition and GPIO#) or three + (condition, GPIO# and gap).If more than three parameters, it can set different or multiple features indicating by type and + the detailed usage is the same as hscfg above. + + where Condition is: + bit 0 = 1 -- broadcast data + bit 1 = 1 -- unicast data + bit 2 = 1 -- mac event + bit 3 = 1 -- multicast data + bit 6 = 1 -- Wakeup when mgmt frame received. + Bit 31 = 1 -- Don't wakeup when IPV6 packet received. + + The host sleep mode will be canceled if condition is set to -1. The default is 0x7. + + where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid + GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO will be used instead). + The default is 0xff. + + where Gap is the gap in milliseconds between wakeup signal and wakeup event or 0xff + for special setting (host acknowledge required) when GPIO is used to wakeup host. + The default is 200. + + The host sleep set except for cancellation will be blocked if host sleep is + already activated. + + where ind_GPIO# is the pin number of GPIO used to indicate wakeup source. The level on + this GPIO will indicate normal wakeup source or abnormal wakeup source. + + where level is used to set level(0/1) on ind_GPIO# pin for indication normal wakeup source. + The opposite level will indicate abnormal wakeup source. The default value is 0. + + where event_force_ignore is a bitmap,each bit represent one wakeup reason event. Set the bit means this + wakeup reason should be force ignored in firmware. Reset the bit means do not handle this reason. + + where event_use_ext_gap is a bitmap, each bit represent one wakeup reason event. Set the bit means this + wakeup reason should use ext_gap to indicate host. Reset the bit means do not handle this reason. + + where event_force_ignore and event_use_ext_gap have the same wakeup reason event definition of each bit: + bit 0 = 1 --Disconnect + bit 1 = 1 --GTK/iGTK rekey failure + bit 2 = 1 --Eapol + other bits --Reserved + They should not set both for one same wakeup reason. + + where ext_gap is the extend gap based on third parameter Gap. Only valid when use_ext_gap is used. + The total gap is (Gap + (x+1)*ext_gap). x means the bit number(start from 0) of this reason in use_ext_gap. + + where gpio_wave is used to set GPIO wave level for hscfg extend. 0 means falling edge, 1 means rising edge. + This parameter is optional and default value is 0. + + where hs_wakeup_interval is used to set host sleep wakeup interval and the type must set to 3 to indicate + this feature. And the value will round to the nearest multiple dtim*beacon_interval in fw. The unit is milliseconds. + + Examples: + mlanutl mlan0 hssetpara -1 : Cancel host sleep mode + mlanutl mlan0 hssetpara 3 : Broadcast and unicast data + Use GPIO and gap set previously + mlanutl mlan0 hssetpara 2 3 : Unicast data + Use GPIO 3 and gap set previously + mlanutl mlan0 hssetpara 2 1 0xa0 : Unicast data + Use GPIO 1 and gap 160 ms + mlanutl mlan0 hssetpara 2 0xff : Unicast data + Use interface (e.g. SDIO) + Use gap set previously + mlanutl mlan0 hssetpara 4 3 0xff : MAC event + Use GPIO 3 + Special host sleep mode + mlanutl mlan0 hssetpara 1 0xff 0xff : Broadcast data + + mlanutl mlan0 hssetpara 2 1 0xa0 1 5 1 : Unicast data + Use GPIO 1 + Gap 160 ms + type=1 to set indication GPIO feature + Use GPIO 5 to indicate wakeup source + High level on GPIO 5 means this is a normal wakeup + mlanutl mlan0 hssetpara 2 1 0xa0 1 5 : Unicast data + Use GPIO 1 + Gap 160 ms + type=1 to set indication GPIO feature + Use GPIO 5 to indicate wakeup source + Use level set previously. + + mlanutl mlan0 hssetpara 2 1 0xa0 2 0 0x1 10 1: Unicast data + Use GPIO 1 + Gap 160 ms + type=2 to set extend hscfg feature + Force_ignore not used + Disconnect will use extend gap to indicate host + Use gap 170. + Rising edge + mlanutl mlan0 hssetpara 2 1 0xa0 2 0x1 0 0 0: Unicast data + Use GPIO 1 + Gap 160 ms + type=2 to set extend hscfg feature + Falling edge + Force ignore Disconnect + Extend gap not used + Not used. + Falling edge + mlanutl mlan0 hssetpara 2 1 0xa0 3 400: Unicast data + Use GPIO 1 + Gap 160 ms + type=3 to set hs_wakeup_interval feature + hs_wakeup_interval set to 400ms + + Note: The parameters will be saved in the driver and be used when host suspends. + The ind_GPIO# and level parameters only work with specific board and firmware. + +mgmtfilter + This command is used to set management frame to wake up host when host suspend. + + Usage: + mlanutl mlanX mgmtfilter <mgmtfilter.conf> + + where <mgmtfilter.conf> + This conf file will set management frame catagory, action and frame mask. + + Examples: + mlanutl mlan0 mgmtfilter mgmtfilter.conf + +auto_arp + This command is used to enable/disable auto arp response in host sleep mode. + No argument is used to get. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + mlanutl mlan0 auto_arp 0 : Disable auto arp response from FW + mlanutl mlan0 auto_arp : Get auto arp configuration status + +htcapinfo + This command is used to configure some of parameters in HTCapInfo IE + (such as Short GI, Channel BW, and Green field support) + + where <m> is <capinfo> + <capinfo> - This is a bitmap and should be used as following + Bit 29: Green field enable/disable + Bit 26: Rx STBC Support enable/disable. (As we support + single spatial stream only 1 bit is used for Rx STBC) + Bit 25: Tx STBC support enable/disable. + Bit 24: Short GI in 40 Mhz enable/disable + Bit 23: Short GI in 20 Mhz enable/disable + Bit 22: Rx LDPC enable/disable + Bit 17: 20/40 Mhz enable disable. + Bit 8: Enable/disable 40Mhz Intolarent bit in ht capinfo. + 0 will reset this bit and 1 will set this bit in + htcapinfo attached in assoc request. + All others are reserved and should be set to 0. + + Setting of any other bits will return error. + + where <n> is <band> + <band> - This is the band info for <capinfo> settings. + 0: Settings for both 2.4G and 5G bands + 1: Settings for 2.4G band + 2: Settings for 5G band + + Example: + mlanutl mlanX htcapinfo + This will display HT capabilties information. + If the information for 2.4G and 5G is different, + the first value is for 2.4G and the second value is for 5G. + Otherwise, it will display a single value for both bands. + + mlanutl mlanX htcapinfo 0x1820000 + This will enable Short GI, Channel BW to 20/40 and disable Green field support for 2.4G and 5G band. + + mlanutl mlanX htcapinfo 0x800000 2 + This will enable Short GI, Channel BW to 20 only, No Rx STBC support and disable Green field support for 5G band. + + The default value is 0x4800000 for 2.4G and 0x5820000 for 5G. + + Note:- This command can be issued any time but it will only come to effect from + next association. (as HTCapInfo is sent only during Association). + +htstreamcfg + This command is used to set/get HT stream configuration. + The setting only takes effect in next association. + + Usage: + mlanutl mlanX htstreamcfg [n] + + where <n> + 0x11: HT stream 1x1 mode + 0x22: HT stream 2x2 mode + + Examples: + mlanutl mlan0 htstreamcfg : Get current setting + mlanutl mlan0 htstreamcfg 0x11 : Set HT stream 1x1 mode + mlanutl mlan0 htstreamcfg 0x22 : Set HT stream 2x2 mode + +httxbfcap + This command is used to set/get the TX beamforming capabilities. + + Usage: + mlanutl mlanX httxbfcap [cap] + + where the parameters are, + cap: TX beamforming capabilities + Bit 0 : Implicit TX BF receiving capable + Bit 1 : RX staggered sounding capable + Bit 2 : TX staggered sounding capable + Bit 3 : RX NDP capable + Bit 4 : TX NDP capable + Bit 5 : Implicit TX BF capable + Bit 6-7 : Calibration + 0: - not supported + 1: - STA can respond to a calibration request using + the CSI Report, but cannot initiate calibration + 2: - reserved + 3: - STA can both initiate and respond to a calibration request + Bit 8 : Explicit CSI TX BF capable + Bit 9 : Explicit non-compressed steering capable + Bit 10 : Explicit compressed steering capable + Bit 11-12: Explicit TX BF CSI feedback + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 13-14: Explicit non-compressed BF feedback capable + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 15-16: Explicit compressed BF feedback capable + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 17-18: Minimal grouping + 0: - no grouping (STA supports groups of 1) + 1: - groups of 1, 2 + 2: - groups of 1, 4 + 3: - groups of 1, 2, 4 + Bit 19-20: CSI number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 21-22: Non-compressed steering number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 23-24: Compressed steering number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 25-26: CSI max number of rows beamformer supported + 0: - single row of CSI + 1: - 2 rows of CSI + 2: - 3 rows of CSI + 3: - 4 rows of CSI + Bit 27-28: Channel estimation capability + 0: - 1 space time stream + 1: - 2 space time streams + 2: - 3 space time streams + 3: - 4 space time streams + Bit 29-31: Reserved + + Examples: + mlanutl mlan0 httxbfcap : Get the current TX BF capabilities + mlanutl mlan0 httxbfcap 0x0000001F : Set the TX BF capabilities of the + Implicit TX BF receiving capable, + RX staggered sounding capable, + TX staggered sounding capable, + RX NDP capable and TX NDP capable + +httxbfcfg + This command is used to configure the TX beamforming options. + Note: Any new subcommand should be inserted in the second + argument and each argument of the sub command should be + separated by semicolon. For global configuration, the + arguments should be separated by space. + + Usage: + mlanutl mlanX httxbfcfg "<action>[;GlobalData/tsData/interval/txPeerData/snrData/txSounding]" + + where the parameters are, + action: TX beamforming action + 0: Control global parameters for beamforming + 1: Performs NDP Sounding for PEER + 2: TX BF interval in milliseconds + 3: Enable/Disable beamforming/sounding for a particular peer + 4: TX BF SNR Threshold for peer + .. <for new subcommand> + GlobalData: Global parameter arguments. + It contains beamforming enable, sounding enable, FB type, snr_threshold + sounding interval, Beamformig mode values seperated by space. + Syntax: + mlanutl mlanX httxbfcfg <action>;<beamforming enable> <sounding enable> <FB type> + <snr_threshold> <sounding interval> <Beamforming mode> + tsData: Trigger sounding for PEER specific arguments, + it contains PEER MAC and status + interval: TX BF interval in milliseconds + txPeerData: Enable/Disable beamforming/sounding for the indicated peer, + it contains PEER MAC, sounding, beamfoming options and FB type; + snrData: TX BF SNR Threshold for peer, it contains PEER MAC and SNR + + Examples: + mlanutl mlan0 httxbfcfg "0" : Get current global configuration parameter + mlanutl mlan0 httxbfcfg "2;00:50:43:20:BF:64" : Get the TX BF periodicity for a given peer + mlanutl mlan0 httxbfcfg "3" : Get the list of MAC addresses that have + beamforming and/or sounding enabled + mlanutl mlan0 httxbfcfg "4" : Get the list of PEER MAC, SNR tuples + programmed into the firmware. + mlanutl mlan0 httxbfcfg "0;0 0 3 10 500 5" : Disable beamforming, sounding, set FB type + to 3, snr threshold to 10, sounding interval + to 500 ms and beamforming mode to 5 + mlanutl mlan0 httxbfcfg "1;00:50:43:20:BF:64" : Perform NDP Trigger sounding to peer + 00:50:43:20:BF:64 + mlanutl mlan0 httxbfcfg "2;00:50:43:20:BF:64;500" : Set TX BF periodicity for peer 00:50:43:20:BF:64 + to 500 milliseconds + mlanutl mlan0 httxbfcfg "3;00:50:43:20:BF:43;1;0;3" : Enable beamforming, disable sounding and set + FB type to 3 for peer 00:50:43:20:BF:43 + mlanutl mlan0 httxbfcfg "4;00:50:43:20:BF:24;43" : Set TX BF SNR threshold to peer + 00:50:43:20:BF:24 with SNR 43 + +httxcfg + This command is used to configure various 11n specific configuration + for transmit (such as Short GI, Channel BW and Green field support) + + where <m> is <txcfg> + This is a bitmap and should be used as following + Bit 15-10: Reserved set to 0 + Bit 9-8: Rx STBC set to 0x01 + BIT9 BIT8 Description + 0 0 No spatial streams + 0 1 One spatial streams supported + 1 0 Reserved + 1 1 Reserved + Bit 7: STBC enable/disable + Bit 6: Short GI in 40 Mhz enable/disable + Bit 5: Short GI in 20 Mhz enable/disable + Bit 4: Green field enable/disable + Bit 3-2: Reserved set to 1 + Bit 1: 20/40 Mhz enable disable. + Bit 0: LDPC enable/disable + + When Bit 1 is set then firmware could transmit in 20Mhz or 40Mhz based + on rate adaptation. When this bit is reset then firmware will only + transmit in 20Mhz. + + where <n> is <band> + <band> - This is the band info for <txcfg> settings. + 0: Settings for both 2.4G and 5G bands + 1: Settings for 2.4G band + 2: Settings for 5G band + + Example: + mlanutl mlanX httxcfg + This will display HT Tx configuration for 2.4G and 5G band. + + mlanutl mlanX httxcfg 0x62 + This will enable 20/40 and Short GI but will disable Green field for 2.4G and 5G band. + + mlanutl mlanX httxcfg 0x30 1 + This will enable Short GI 20 Mhz and Green field for 2.4G band. + + The default value is 0x20 for 2.4G and 0x62 for 5G. + + Note:- If 20/40 MHz support is disabled in htcapinfo, device will not transmit + in 40 MHz even 20/40 MHz is enabled in httxcfg. + +inactivityto + This command is used to set/get the inactivity timeout value, which specifies + when WLAN device is put to sleep. + + Usage: + mlanutl mlanX inactivityto <n> <m> <l> [k] + + where the parameter are: + <n>: timeout unit in microseconds. + <m>: Inactivity timeout for unicast data. + <l>: Inactivity timeout for multicast data. + [k]: Inactivity timeout for new Rx traffic after PS notification to AP. + + Examples: + mlanutl mlan0 inactivityto : Get the timeout value + mlanutl mlan0 inactivityto 1000 2 3 : Set timeout unit to 1000 us (1 ms), + inactivity timeout for unicast data is 2 ms, + inactivity timeout for multicast data is 3 ms + +ipaddr + This command is used to set/get IP address. + + Usage: + mlanutl mlanX ipaddr ["<op>;<ipaddr>"] + + where <op> + 0: Remove the IP address + bit 0: Set IP address for broadcast ARP filter, which will be auto enabled + in next host sleep configuration + bit 1: Set IP address for auto broadcast ARP response + + Examples: + mlanutl mlan0 ipaddr : Get current settings + mlanutl mlan0 ipaddr "0" : Remove IP address + mlanutl mlan0 ipaddr "1;192.168.0.5" : Set IP address for ARP filter + mlanutl mlan0 ipaddr "3;192.168.0.6" : Set IP address for ARP filter + : and auto ARP response + +linkstats + This command is used to get the link statistics from the firmware. + + Usage: + mlanutl mlanX linkstats + +listeninterval + This command is used to set/get listen interval in assoc request. + + Usage: + mlanutl mlanX listeninterval [l] + + where the parameter: + [l]: Value of listen interval [Default 10] + + Examples: + mlanutl mlan0 listeninterval : Display Listen interval + mlanutl mlan0 listeninterval 1 : Set Listen interval to 1. + +macctrl + This command is used to set/get MAC control. + It's recommended to read the current setting first to avoid override issue. + + Usage: + mlanutl mlanX macctrl [n] + + where <n> + bit 0: Rx enabled + bit 1: Directed Filter enabled + bit 2: LoopBack enabled + bit 3: WEP enabled + bit 4: EthernetII enabled + bit 5: MultiCast enabled + bit 6: BroadCast enabled + bit 7: Promiscuous enabled + bit 8: All MultiCast enabled + bit 9: RTS/CTS enabled (0: CTS to self) + bit 10: Enforce Protection enabled + bit 11: Force 11N Protection enabled + bit 12: Rx 802.11 Packets enabled + bit 13: Ad-hoc g Protection enabled + bit 14: Reserved + bit 15: WEP Type + bit 16: BandWidth Indication in RTS enabled + bit 17: Dynamic BandWidth Indication Mode in RTS enabled + bit 18-31: Reserved + + Examples: + mlanutl mlan0 macctrl : Get current MAC control + mlanutl mlan0 macctrl 0x13 : Set Rx enabled and Directed Filter enabled and EthernetII enabled + mlanutl mlan0 macctrl 0x813 : Set Rx enabled and Directed Filter enabled and EthernetII enabled + Force 11N Protection enabled + +mefcfg + This command is used to set MEF settings. + + Usage: + mlanutl mlanX mefcfg <mef.conf> + + Where the parameter is: + mef.conf : The configuration file specifying the MEF settings. + + Example: + mlanutl mlan0 mefcfg config/mef.conf + +memrdwr + This command is used to read/write the adapter memory. + + Usage: + mlanutl mlanX memrdwr <address> [value] + + where the parameters are, + <address>: memory address + [value]: value to be written + + Examples: + mlanutl mlan0 memrdwr 0x4cf70 : Read memory address 0x4cf70 + mlanutl mlan0 memrdwr 0x80000000 0xffffffff + : Write 0xffffffff to memory address 0x80000000 + +miracastcfg + This command is used to set/get the miracast configuration. + + Usage: + mlanutl mlanX miracastcfg [l] [m] [n] + + where the parameters are, + [l]: miracast mode + 0: Disable + 1: Source + 2: Sink + [m]: scan time per channel, in ms + [n]: gap during two scans, in ms + + Examples: + mlanutl mlan0 miracastcfg : Get miracast configuration + mlanutl mlan0 miracastcfg 0 : Disable miracast configuration + mlanutl mlan0 miracastcfg 1 20 40 : Set miracast mode as source, with scan time + 20ms per channel and gap during two scans 40ms + +mgmtframectrl + This command is used to set/get registered frame type to passthrough. + + Usage: + mlanutl mlanX mgmtframectrl [<mask>] + mlanutl uapX mgmtframectrl [<mask>] + + Where the parameter is: + <mask> : the bit mask of management frame reception. + : Bit 0 - Association Request + : Bit 1 - Association Response + : Bit 2 - Re-Association Request + : Bit 3 - Re-Association Response + : Bit 4 - Probe Request + : Bit 5 - Probe Response + : Bit 8 - Beacon Frames + + Examples: + mlanutl mlan0 mgmtframectrl : Get present mask + mlanutl mlan0 mgmtframectrl 0x0020 : Bit 5 is set, Forward probe response frames to application layer + +mgmtframetx + This command is used to send management frame. + + Usage: + mlanutl mlanX mgmtframetx <mgmt_frame.conf> + + Where the parameter is: + mgmt_frame.conf : The configuration file contains the management frame. + + Examples: + mlanutl mlan0 mgmtframetx config/mgmt_frame.conf + +mpactrl + This command is used to set/get the Tx, Rx SDIO aggregation parameters. + Note: The parameters can be set only in disconnected state. + + Usage: + mlanutl mlanX mpactrl [tx_ena] [rx_ena] [tx_size] [rx_size] [tx_ports] [rx_ports] + + where the parameter are: + [tx_ena]: Enable/disable (1/0) Tx MP-A + [rx_ena]: Enable/disable (1/0) Rx MP-A + [tx_size]: Size of Tx MP-A buffer + [rx_size]: Size of Rx MP-A buffer + [tx_ports]: Max ports (1-16) for Tx MP-A + [rx_ports]: Max ports (1-16) for Rx MP-A + default values are 1 1 16384 32768 16 16 + The MP-A may be disabled by default at build time if the MMC driver byte mode patch + is not available in kernel. + + Examples: + mlanutl mlan0 mpactrl : Get MP aggregation parameters + mlanutl mlan0 mpactrl 0 0 + : Disable MP aggregation for Tx, Rx respectively + mlanutl mlan0 mpactrl 1 1 8192 8192 8 8 + : Enable MP aggregation for Tx, Rx + : Set Tx, Rx buffer size to 8192 bytes + : Set maximum Tx, Rx ports to 8 + +netmon + This command is used to set/get sniffer mode configuration. + Note: The channel and band config is optional. If not specified, or if + any STA/uAP/STA+uAP connection is active, sniffer activity will be started + on the current config set in the FW. + 'rtap' monitor interface will be created on enabling sniffer activity and + should be made 'up' for capturing in a sniffer app. + + Usage: + mlanutl <interface> netmon [<act> [<filter>]] + mlanutl mlanX netmon [<act> [<filter>] [<band> <chan> [offset]]] + + Where the parameters are: + <interface> : mlanX, uapX + <act> : (0) disable sniffer activity + : (1) enable sniffer activity + + <filter> : network monitor fitler flag + bit 0: (1/0) enable/disable management frame + bit 1: (1/0) enable/disable control frame + bit 2: (1/0) enable/disable data frame + bit 3: (1/0) enable/disable frames destined to active connection only + bit 4: (1/0) enable/disable decrypted unicast data/mgmt frames + + <band> : 802.11 band + bit 0: B + bit 1: G + bit 2: A + bit 3: GN + bit 4: AN + bit 5: AC 2.4G + bit 6: AC 5G + <chan> : channel to monitor + + [offset] : secondary channel bandwidth + 0 - Bandwidth 20Mhz + 1 - HT Bandwidth 40Mhz sec channel above + 3 - HT Bandwidth 40Mhz sec channel below + 4 - VHT Bandwidth 80Mhz + + Examples: + mlanutl mlan0 netmon : Get the current sniffer mode configuration + mlanutl mlan0 netmon 0 : Disable network monitor activity + mlanutl uap0 netmon 1 7 : Enable sniffer activity on current channel set in FW, + set filter data, control, management frame. + mlanutl mlan0 netmon 1 4 11 6 : Enable sniffer activity in absence of active connection, + set filter data frame, band B/G/GN and channel 6 + mlanutl mlan0 netmon 1 7 20 64 1 : Enable sniffer activity in absence of active connection + set filter management, control and data frame, band A/AN, + channel 64 and secondary channel above + mlanutl uap0 netmon 1 0x0c : Enable sniffer activity, set filter data frames + destined to the active uAP connection only + + mlanutl mlan0 netmon 1 0x1d : Enable sniffer activity, set filter decrypted data and + management frames destined to the active STA connection + only + +offchannel + This command is used to set/cancel the offchannel configuration. + Note: This command only can be used when cfg80211 is enabled during load time. + + Usage: + mlanutl mlanX offchannel [<l> <m> <n>] + + where + <l> + 0 : Cancel the offchannel configuration + 1 : Set the offchannel configuration + <m> + The channel to configure + <n> + The duration for which to configure + + Examples: + mlanutl mlan0 offchannel : Get current offchannel status. + mlanutl mlan0 offchannel 0 : Cancel the offchannel configuration. + mlanutl mlan0 offchannel 1 3 5 : Configure channel 3 for 5 milliseconds. + mlanutl mlan0 offchannel 1 36 5000 : Configure channel 36 for 5000 milliseconds. + +otpuserdata + This command is used to get the OTP user data. + + Where + <l> is <user_data_length> + <user_data_length> - This parameter specifies the length of OTP user data to be read + + Examples: + mlanutl mlan0 otpuserdata 10 : Get the 10-byte OTP user data + +passphrase + This command is used to set/get passphrase for WPA-PSK/WPA2-PSK mode. + + Where <l> + ASCII string for ssid/passphrase/psk. + + 1) "0;<ssid=valid ssid>" - This will get the passphrase, AKMP + for specified ssid, if none specified then it will get all. + + Example: + mlanutl mlan0 passphrase "0;ssid=marvell" + + 2) "1;<psk=64 byte hexpsk>;<passphrase=1-63 byte passphare> + <ssid=valid ssid>" - Passphrase and psk cannot be provided for the same SSID. + This command takes only one SSID at a time, If ssid= is present it should contain + a passphrase or psk. If no arguments are provided then AKMP=802.1x, and passphrase + should be provided after association. + End of each parameter should be followed by a ';'(except for the last parameter) + as the delimiter. If ';' or '/' has to be used in an SSID then a '/' should be preceded + to ';' or '/' as a escape. + + Examples: + mlanutl mlan0 passphrase "1;ssid=mrvlAP;passphrase=abcdefgd" + mlanutl mlan0 passphrase "1;ssid=mrvl AP;psk=<64 bytes hexpsk>" + + If user wants to input the ssid as "mrvl; AP" then command has to be + mlanutl mlan0 passphrase "1;ssid=mrvl/; AP;passphrase=abcdefgh" + + If user wants to input the ssid as "//;" then command has to be + mlanutl mlan0 passphrase "1;ssid=/////;;passphrase=abcdefgh" + + 3) "2;<ssid=valid ssid>" - This will clear the passphrase + for specified ssid, if none specified then it will clear all. + + Examples: + mlanutl mlan0 passphrase "2;ssid=marvell" + mlanutl mlan0 passphrase "2" : Clear all profiles and disable embedded supplicant + +pb_bypass + This command is used to get the By-passed TX packet from upper layer. + + Usage: + + mlanutl mlanX pb_bypass [data_1, data_2, ... data_n] + + where value of data_1, data_2, ... data_n isBypass TX Data + +pmfcfg + This command is used to set/get management frame protection parameters. + + Usage: + mlanutl mlanX pmfcfg <m> <n> + + where + <m>: Management Frame Protection Capable (MFPC) + 1: Management Frame Protection Capable + 0: Management Frame Protection not Capable + <n>: Management Frame Protection Required (MFPR) + 1: Management Frame Protection Required + 0: Management Frame Protection Optional + Default setting is PMF not capable. + m = 0, n = 1 is an invalid combination + + Examples: + mlanutl mlan0 pmfcfg : Get PMF parameters + mlanutl mlan0 pmfcfg 1 0 : Set MFPC and make MFPR optional + +port_ctrl + This command is used to Set/Get Port Control mode. No argument is used to get. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + mlanutl mlan0 port_ctrl 1 : Enable Port Control mode + mlanutl mlan0 port_ctrl : Get Port Control mode status + +powercons + This command is used to set the local transmit power constraint. + Value is in dbm unit. This command is only used for ad-hoc start. + + Usage: + mlanutl mlanX powercons [n] + + Examples: + mlanutl mlanX powercons : get the current setting + mlanutl mlanX powercons 12 : set local power constraint to 12 dbm + +pscfg + This command is used to set/get PS configuration parameters. + + Usage: + mlanutl mlanX pscfg [k] [d] [l] ... + + Where the parameters: + [k]: Keep alive null packet interval (0: Unchanged, -1: Disable, n: Interval in seconds) + [d]: DTIM interval ( 0: Unchanged, + 1-5: Value, + 65534: DTIM will be ignored, listen interval will be used, + 65533: Closest DTIM to the listen interval period will be used ) + [l]: Local listen interval ( 0: Unchanged, + -1: Disable, + 1-49: Value in beacon intervals, + >= 50: Value in TUs ) + [a]: Ad-hoc awake period (0: Unchanged, 1-31: Beacon interval, 255: Firmware + will go to sleep after beacon send out) + [b]: Beacon miss timeout (0: Unchanged, 1-50: Value in milliseconds, 65535: Disable) + [p]: Delay to PS (0-65535: Value in milliseconds, default 1000ms) + [m]: PS mode (0: Unchanged, 1: Auto mode, 2: PS-Poll mode, 3: PS Null mode) + No change if parameters are not provided. + + Examples: + mlanutl mlan0 pscfg : Get all the current PS configuration settings + mlanutl mlan0 pscfg 3 4 : Set PS keep alive null packet interval to 3 seconds + and DTIM interval to 4, all the other configurations + are unchanged + mlanutl mlan0 pscfg 0 0xfffe 10 0 20 + : Disable DTIM interval, set local listen interval to + 10 beacon intervals and beacon miss interval to 20, + all the other configurations are unchanged + mlanutl mlan0 pscfg 0 0 0 0 0 50 : Set delay to PS to 50 ms, keep the others unchanged + +bcntimeoutcfg + This command is used to set Beacon timeout parameters. + + Usage: + mlanutl mlanX bcntimeoutcfg [l] [m] [o] [p] + + Where the parameters: + [l]: Beacon miss timeout period Rx window (in ms) + [m]: Beacon miss timeout period (unit in beacon interval) + [o]: Beacon reacquire timeout period Rx window (unit in beacon interval) + [p]: Beacon reacquire timeout period (unit in beacon interval) + Please note that it would be better [m]+[p] not exceed 64. + Examples: + mlanutl mlan0 bcntimeoutcfg 10 30 2 30 : Set beacon timeout configure to + Beacon miss timeout period Rx window : 10 (ms) + Beacon miss timeout period : 30 (Beacon Interval) + Beacon reacquire timeout period Rx window : 2 (Beacon Interval) + Beacon reacquire timeout period : 30 (Beacon Interval) + +psmode + This command is used to set/get the IEEE PS mode configuration. + + Usage: + mlanutl mlanX psmode [l] + + where the parameter: + [l] + 0 : Disable IEEE PS mode + 1 : Enable IEEE PS mode + <none>: Get IEEE PS mode + + Examples: + mlanutl mlan0 psmode : Get IEEE PS mode. + mlanutl mlan0 psmode 1 : Enable IEEE PS mode. + +qconfig + Send a WMM AC Queue configuration command to get/set/default params + + Configure or get the parameters of a WMM AC queue. The command takes + an optional Queue Id as a last parameter. Without the queue id, all + queues will be acted upon. + + Usage: + mlanutl mlanX qconfig def [Queue Id: 0-3] + mlanutl mlanX qconfig get [Queue Id: 0-3] + mlanutl mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3] + +qoscfg + This command sets WMM IE QOS info when an argument is given, and gets current WMM + IE QOS info when no argument is given. + + Examples: + mlanutl mlanX qoscfg 0x0f : Set WMM IE QOS info to 0x0f + mlanutl mlanX qoscfg : Get WMM IE QOS info + +qstatus + This command retrieves the current status of the WMM queues. If WMM + is enabled then it displays the information for each AC in a table. + + Usage: + mlanutl mlanX qstatus + +radioctrl + This command is used to turn on/off the radio. + Note: The radio can be disabled only in disconnected state. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + mlanutl mlan0 radioctrl 1 : Turn the radio on + mlanutl mlan0 radioctrl : Get radio status + +rdeeprom + This command is used to read the EEPROM contents of the card. + + Usage: + mlanutl mlanX rdeeprom <offset> <length> + + where the parameters are, + <offset>: multiples of 4 + <length>: 4-20, multiples of 4 + + Example: + mlanutl mlan0 rdeeprom 0 20 : Read 20 bytes of EEPROM data from offset 0 + +reassoctrl + This command is used to turn on/off re-association in driver. + + Usage: + mlanutl mlanX reassoctrl [n] + + Where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + mlanutl mlan0 reassoctrl : Get re-association status + mlanutl mlan0 reassoctrl 1 : Turn re-association on + +regioncode + This command is used to set/get the region code in the station. + Note: This command should be issued at beginning before band/channel selection + and association. + + where value is 'region code' for various regions like + USA FCC, Canada IC, Europe ETSI, Japan ... + The special code (0xff) is used for Japan to support channel 1-14 in B/G/N mode. + + Examples: + mlanutl mlan0 regioncode : Get region code + mlanutl mlan0 regioncode 0x10 : Set region code to USA (0x10) + Note : in some case regioncode will be 0 after updated countycode or 80211d + i.e. mlanutl mlanX countrycode (CA, JP, CN, DE, ES AT, BR, RU) + or uaputl.exe sys_cfg_80211d state 1 country (CA, JP, CN, DE, ES AT, BR, RU) + Please use cfp instead of it. + +regrdwr + This command is used to read/write the adapter register. + + Usage: + mlanutl mlanX regrdwr <type> <offset> [value] + + where the parameters are, + <type>: 1:MAC/SOC, 2:BBP, 3:RF, 5:CAU, 6:PSU + <offset>: offset of register + [value]: value to be written + Note: + BBP reg (type 2) 0xXZZZ: + X: 0=BBUD, 8=BBUA. + ZZZ: offset (0-0xFFF). + RF reg (type 3) 0xXYZZ: + X = Path ID (0-1) + Y = Page Number (0-6) in selected Path + ZZ = Register offset in selected path/page + + Examples: + mlanutl mlan0 regrdwr 1 0xa060 : Read the MAC register + mlanutl mlan0 regrdwr 1 0xa794 0x80000000 + : Write 0x80000000 to MAC register + +rejectaddbareq + This command is used to set/get the conditions of rejecting addba request. + + Usage: + mlanutl mlanX rejectaddbareq [conditions] + mlanutl uapX rejectaddbareq [conditions] + + Where conditions are: + bit 0 = 1 -- reject the addba request when host sleep activated + others -- reserved + + Examples: + mlanutl mlan0 rejectaddbareq : Get the reject addba request conditions + mlanutl mlan0 rejectaddbareq 0x1 : Reject the addba request + when host sleep activated + mlanutl uap0 rejectaddbareq 0x1 : Reject the addba request + when host sleep activated + +scancfg + This command is used to set/get scan configuration parameters. + + Usage: + mlanutl mlanX scancfg [t] [m] [p] [s] [a] [b] [ext] + + where the parameters: + [t]: Scan Type (0: Unchanged, 1: Active, 2: Passive, default Active) + [m]: Scan Mode (0: Unchanged, 1: BSS, 2: IBSS, 3: Any, default Any) + [p]: Scan Probes (0: Unchanged, 1-4: Number of probes per channel, default 4) + [s]: Specific Scan Time (0: Unchanged, n: Value in ms, default 110 ms, max 500 ms) + [a]: Active Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 500 ms) + [b]: Passive Scan Time (0: Unchanged, n: Value in ms, default 200 ms, max 2000 ms) + [ext]: Extended scan (0: Legacy scan, 1: Extended scan) + No change if the parameter is 0 or the parameter is not provided. + + Examples: + mlanutl mlan0 scancfg : Get all the current scan configuration settings + mlanutl mlan0 scancfg 1 3 : Set scan type to active and scan mode to any, + all the other scan configurations are unchanged + mlanutl mlan0 scancfg 0 1 2 200 : Set scan mode to BSS, number of probes to 2 and + specific scan time to 200 ms, all the other scan + configurations are unchanged + +sdcmd52rw + This command is used to read/write a controller register in + Secure Digital I/O Interfaces. + + Usage: + mlanutl mlanX sdcmd52rw <function number> <register address> [value] + + For SDIO MMC driver, only function 0 and 1 access is allowed. And there + is a limitation for function 0 write, only vendor specific CCCR registers + (0xf0 -0xff) are permiited. + + Examples: + mlanutl mlan0 sdcmd52rw 1 3 : Read SDIO function 1 register 3 + mlanutl mlan0 sdcmd52rw 1 1 0x3f : Write 0x3f to SDIO function 1 register 1 + +sdcmd53rw + This command is used to issue a CMD53 read/write data in + Secure Digital I/O Interfaces. + + Usage: + mlanutl mlanX sdcmd53rw <func> <address> <mode> <blksize> <blknum> [data1] ... [dataN] + + where the parameters are, + <func>: function number (0/1/2/..) + <address>: data address + <mode>: byte mode/block mode (0/1) + <blksize>: block size (32/64/../512, NA for byte mode) + <blknum>: block number or byte number + <data1> ... <dataN>: data for write + + Note: The total data length is block size * block number for block mode + or byte number for byte mode. The max data length is 2000-byte. + For write the data pattern will be duplicated to data buffer. + + Examples: + mlanutl mlan0 sdcmd53rw 0 0x8000 1 0x40 2 + mlanutl mlan0 sdcmd53rw 1 0x10000 0 1 5 0x0a 0x0b 0x0c 0x0d 0x0e + +sdioclock + Turn On(1) or Off(0) the SDIO clock. + + Usage: + mlanutl mlanX sdioclock 1 (on) + mlanutl mlanX sdioclock 0 (off) + mlanutl mlanX sdioclock (get the current clock state) + +setuserscan + Initiate a customized scan and retrieve the results + + Usage: + mlanutl mlanX setuserscan [ARGS] + + Where [ARGS]: + ssid="[SSID]" specify a SSID filter for the scan + chan=[chan#][band][mode] where band is [a,b,g,n] and mode is + blank for unchange, or 'c' for active or 'p' for passive + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + wc="[WILDCARD SSID]" specify a UNIX pattern matching filter (using * + and ?) for SSIDs found in a broadcast probe + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + gap=[gap time] Time gap between two scans in milliseconds + probes=[#] number of probe requests to send on each chan + for each broadcast probe required and each SSID + specific probe required (1-4) + bss_type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + sort_by_ch Sort by channel number in ascending order. + Default mode: Sort by Signal Strength in descending order. + + Any combination of the above arguments can be supplied on the command line. + If the chan token is absent, a full channel scan will be completed by driver. + If the dur or probes tokens are absent, the driver default setting will be + used. The bssid and ssid fields, if blank, will produce an unfiltered scan. + It's allowed to input multiple ssid/wc entries, the max entry number is 10. + The type field will default to 3 (Any) and the keep field will default to 0 + (Discard). + + Examples: + 1) Perform an active scan on channels 1, 6, and 11 in the 'g' band: + setuserscan chan=1g,6g,11g + + 2) Perform a passive scan on channel 11 for 20 ms: + setuserscan chan=11gp dur=20 + + 3) Perform an active scan on channels 1, 6, and 11; and a passive scan on + channel 36 in the 'a' band: + setuserscan chan=1g,6g,11g,36ap + + 4) Perform an active scan on channel 6 and 36 for specific SSID: + setuserscan chan=6g,36a ssid=TestAP1 ssid=TestAP2 + + 5) Scan all available channels (B/G/N, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + setuserscan bssid=00:50:43:20:12:82 keep=1 + + 6) Scan channel 6, for all infrastructure networks, sending two probe + requests. Keep the previous scan table intact. Update any duplicate + BSSID/SSID matches with the new scan data: + setuserscan chan=6g bss_type=1 probes=2 keep=1 + + 7) Scan channel 1 and 6, for all networks matching the Mrvl*AP + or AP*Mrvl? patterns and for MrvlTst SSID. Generate 3 broadcast + probes for the patterns and 3 SSID specific probes for MrvlTst on + both channel 1 and channel 6. + setuserscan chan=1g,6g probes=3 wc="Mrvl*AP" wc="AP*Mrvl?" ssid="MrvlTst" + + 8) Scan all the channels for specified band. + setuserscan chan=0g + + 9) Scan channel 1 and 6, send 3 probe requests, scan each channel for 40 ms + with time gap of 50ms between 2 scans + setuserscan chan=1g,6g probes=3 dur=40 gap=50 + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. +cancelscan + This command is used to cancel scan + Usage: + mlanutl mlanX cancelscan +sleepparams + This command is used to set the sleepclock configurations + + Usage: + mlanutl mlanX sleepparams [<p1> <p2> <p3> <p4> <p5> <p6>] + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + + Examples: + mlanutl mlan0 sleepparams : Get current sleepclock configuration + mlanutl mlan0 sleepparams 10 1000 2000 1 0 128 : Set sleepclock configuration + +sleeppd + This command is used to configure the sleep period of the WLAN device. + + Usage: + mlanutl mlanX sleeppd [<period>] + + Where the parameter is: + period: sleep period in milliseconds. Range 10~60. 0 for disable. + + Examples: + mlanutl mlan0 sleeppd : Get sleep period configuration + mlanutl mlan0 sleeppd 10 : Set sleep period to 10 ms + +sysclock + This command is used to set/get system clocks in MHz. + The current system clock, configurable system clocks and all of the + supported system clocks will be returned if no parameter provided. + + Examples: + mlanutl mlan0 sysclock : Get system clocks + 80 80 128 128 128 5 11 16 20 22 32 40 44 64 80 106 128 160 ... + (The current system clock is 80 MHz. + The configurable system clocks of non-security, security, non-security + A-MPDU and security A-MPDU are 80 MHz, 128 MHz, 128 MHz and 128 MHz. + The supported system clocks are 5 MHz, 11 MHz, ..., 160 MHz, 182 MHz, + 213 MHz, 256 MHz, 320 Mhz, 366 MHz , ... . the Max system clocks is different + for different chips, you could use this command to get the supported system clock) + + mlanutl mlanX sysclock 80 : Set system clock in non-security mode + to 80 MHz, no change for others + mlanutl mlanX sysclock 0 0 128 : Set system clock in non-security A-MPDU + mode to 128 MHz, no changes for others + +host_tdls_config + This command is used to support channel switch and uapsd for host based tdls + + Usage: + mlanutl mlanX host_tdls_config <host_tdls.conf> + + + Where the parameter is: + host_tdls.conf: The configuration file specifying to enable/disable uapsd/cs and related parameters. + + Examples: + mlanutl mlan0 host_tdls_config config/host_tdls.conf + : enable or disable uapsd/cs, config the channel related ie, based on the configuration file. +tdls_channel_switch + This command is used to send TDLS channel switch request. + + Usage: + mlanutl mlanX tdls_channel_switch <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file for sending TDLS channel switch command. + + Examples: + mlanutl mlan0 tdls_channel_switch config/tdls.conf + : Send TDLS channel switch command, based on the configuration file. + +tdls_config + This command is used to enable/disable TDLS on device. + + Usage: + mlanutl mlanX tdls_config <0/1> + + Where the parameter is: + 0: Enable TDLS. + 1: Disable TDLS. + + Examples: + mlanutl mlan0 tdls_config 0 : Disable TDLS + mlanutl mlan0 tdls_config 1 : Enable TDLS + +tdls_cs_params + This command is used to set TDLS channel switch params + + Usage: + mlanutl mlanX tdls_cs_params <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file specifying the TDLS channel switch params. + + Examples: + mlanutl mlan0 tdls_cs_params config/tdls.conf + : Set TDLS channel switch params, based on the configuration file. + +tdls_debug + This command is used for FW debug functionality and tests. + +tdls_disable_cs + This command is used to disable TDLS channel switch + + Usage: + mlanutl mlanX tdls_disable_cs <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file to disable TDLS channel switch. + + Examples: + mlanutl mlan0 tdls_disable_cs config/tdls.conf + : Disable TDLS channel switch, based on the configuration file. + +tdls_discovery + This command is used to request TDLS discovery. + + Usage: + mlanutl mlanX tdls_discovery <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file to request TDLS discovery. + + Examples: + mlanutl mlan0 tdls_discovery config/tdls.conf + : Request TDLS discovery based on the configuration file. + +tdls_link_status [peer_mac_address] + This command is used to get link information about TDLS links or + a TDLS link correponding to peer mac address. + + Usage: + mlanutl mlanX tdls_link_status <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file to send TDLS command to get current link status. + + Examples: + mlanutl mlan0 tdls_link_status config/tdls.conf + : Send TDLS command to get current link status based on the configuration file. + +tdls_powermode + This command is used to send TDLS powermode request. + + Usage: + mlanutl mlanX tdls_powermode <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file for sending TDLS powermode command. + + Examples: + mlanutl mlan0 tdls_powermode config/tdls.conf + : Send TDLS powermode (either 0:Active, 1:PowerSave) command, based on the configuration file. + +tdls_setinfo + This command is used for setting the capabilities of the TDLS station. + + Usage: + mlanutl mlanX tdls_setinfo <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file specifying the capabilities of the TDLS station. + + Examples: + mlanutl mlan0 tdls_setinfo config/tdls.conf + : Set capabilities of the TDLS station, based on the configuration file. + +tdls_setup + This command is used to send TDLS setup request. + + Usage: + mlanutl mlanX tdls_setup <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file for sending TDLS setup request. + + Examples: + mlanutl mlan0 tdls_setup config/tdls.conf + : Send TDLS setup request, based on the configuration file. + +tdls_stop_channel_switch + This command is used to send stop TDLS channel switch request. + + Usage: + mlanutl mlanX tdls_stop_channel_switch <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file for sending stop TDLS channel switch command. + + Examples: + mlanutl mlan0 tdls_stop_channel_switch config/tdls.conf + : Send stop TDLS channel switch command, based on the configuration file. + +tdls_teardown + This command is used to send TDLS teardown request. + + Usage: + mlanutl mlanX tdls_teardown <tdls.conf> + + Where the parameter is: + tdls.conf: The configuration file for requesting teardown of TDLS link. + + Examples: + mlanutl mlan0 tdls_teardown config/tdls.conf + : Request teardown of TDLS link, based on the configuration file. + +thermal + This command is used to get the current thermal reading. + + Examples: + mlanutl mlan0 thermal : Get thermal reading + +ts_status + This command queries the FW for the status of TSIDs 0 through 7 + configured via call admission control and displays the results in a + table. + + Usage: + mlanutl mlanX ts_status + +tsf + Get the TSF timer value for the station. Station maintains a TSF timer with + modulus 2^64 counting in increments of microseconds. + + Usage: + mlanutl mlanX tsf + +txaggrctrl + This command is used to enable/disable TX AMPDU on infra link when TDLS link is established + + Usage: + mlanutl mlanX txaggrctrl [m] + + Where: + [m]: 1 to enable TX AMPDU on infra link; 0 to disable TX AMPDU on infra link + + Examples: + mlanutl mlan0 txaggrctrl : Get current TX AMPDU status on infra link + mlanutl mlan0 txaggrctrl 0 : Disable TX AMPDU on infra link + mlanutl mlan0 txaggrctrl 1 : Enable TX AMPDU on infra link + + Note: + The set command only works when TDLS link is established. + +txbufcfg + This command can be used to get current buffer size. + + eg: + mlanutl mlanX txbufcfg - This will display the current buffer size. + + Note:- The actual tx buf size will depends on AP's capability and max transmit buffer size. + +txratecfg + This command is used to set/get the transmit data rate. + + Note: + 1) The data rate can be set only after association. + + 2) If the reassoc is OFF driver reset the data rate to auto if the connection state is disconnected. + Please note that user has to re-issue the set data rate command if the driver is disconnected. + + 3) If the reassoc is ON driver remembers the data rate set by the user, if the driver is + disconnected user does not have to re-issue the set data rate again. + + Where + [l] is <format> + <format> - This parameter specifies the data rate format used in this command + 0: LG + 1: HT + 2: VHT + 0xff: Auto + + [m] is <index> + <index> - This parameter specifies the rate or MCS index + If <format> is 0 (LG), + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 6 Mbps + 5 9 Mbps + 6 12 Mbps + 7 18 Mbps + 8 24 Mbps + 9 36 Mbps + 10 48 Mbps + 11 54 Mbps + If <format> is 1 (HT), + 0 MCS0 + 1 MCS1 + 2 MCS2 + 3 MCS3 + 4 MCS4 + 5 MCS5 + 6 MCS6 + 7 MCS7 + 8 MCS8 + 9 MCS9 + 10 MCS10 + 11 MCS11 + 12 MCS12 + 13 MCS13 + 14 MCS14 + 15 MCS15 + If <format> is 2 (VHT), + 0 MCS0 + 1 MCS1 + 2 MCS2 + 3 MCS3 + 4 MCS4 + 5 MCS5 + 6 MCS6 + 7 MCS7 + 8 MCS8 + 9 MCS9 + [n] is <nss> + <nss> - This parameter specifies the NSS. It is valid only for VHT + If <format> is 2 (VHT), + 1 NSS1 + 2 NSS2 + + Examples: + mlanutl mlan0 txratecfg : Read the current data rate setting + mlanutl mlan0 txratecfg 0 3 : Set fixed Tx rate to 11 Mbps + mlanutl mlan0 txratecfg 0 11 : Set fixed Tx rate to 54 Mbps + mlanutl mlan0 txratecfg 1 3 : Set fixed Tx rate to MCS3 + mlanutl mlan0 txratecfg 2 3 2 : Set fixed Tx rate to MCS3 for NSS2 + mlanutl mlan0 txratecfg 0xff : Disable fixed rate and uses auto rate + +verext + Retrieve and display an extended version string from the firmware + + Usage: + mlanutl mlanX verext [#] + + where [#] is an optional argument to retrieve a specific version string, + omission of the argument retrieves the 0 indexed string. + +version + This is used to get the current version of the driver and the firmware. + +vhtcfg + This command is used to set and get various 11ac specific configuration + for transmission and reception. For the SET operation, all paramaters + may be applied. For the GET operation, only the first two parameters are applied. + The 6th argument "rx_mcs_set" can be used to disbale/enable 802.11ac. + + where <j> is <band> + <band> - This is the band setting for the vhtcfg + 0: Settings for both 2.4G and 5G bands (for SET operation, 11N BW only) + 1: Settings for 2.4G band (for 11N BW only) + 2: Settings for 5G band + + where <k> is <txrx> + <txrx> - This parameter specifies the configuration of VHT operation for TX or/and VHT capabilities + 0: Unspecified + 1: configuration of VHT capabilities for Tx operations (STA only) + 2: configuration of VHT capabilities for association (STA only) + 3: configuration of VHT capabilities (uAP only) + Note: For the STA, the VHT capabilities configuration is applied in association, + whereas the VHT operations configuration is actually used in Tx. + + where [l] is <bwcfg> + <bwcfg> - This parameter specifies the bandwidth (BW) configuration + applied to the vhtcfg. + If <txrx> is 1/3 (Tx operations), + 0: Tx BW follows the BW (20/40 MHz) from 11N CFG + 1: Tx BW follows the BW (80/160/80+80 MHz) from VHT Capabilities + defined in <vhtcap> below for 5G band. + If <txrx> is 2 (association), + 0: Rx BW follows the BW (20/40 MHz) from 11N CFG + 1: Rx BW follows the BW (80/160/80+80 MHz) from VHT Capabilities + defined in <vhtcap> below for 5G band. + + where [m] is <vhtcap> + <vhtcap> - This parameter specifies the VHT capabilities info if <txrx> is 2 (association) + or the VHT Tx operations if <txrx> is 1 (Tx operations). + The VHT Tx operation should be a subset of VHT capabilities for association. + It is a bitmap and should be used as follows: + + Bit 31-30: Reserved and set to 0 + Bit 29: TX antenna pattern consistency + 1: antenna pattern does not change + 0: antenna pattern might change + Bit 28: RX antenna pattern consistency + 1: antenna pattern does not change + 0: antenna pattern might change + Bit 27-26: VHT link adaptation capable + 0: no feedback of VHT MFB from the STA + 1: unsolicted feedback of VHT MFB from the STA + 2: both response and unsolicted feedback of VHT MFB + from the STA + 3: reserved and set to 0 + Bit 25-23: Maximum A-MPDU length exponent + Bit 22: +HTC-VHT capable (1: enable. 0 disable) + Bit 21: VHT TXOP PS + Bit 20: MU beamformee capable (1: enable. 0 disable) + Bit 19: MU beamformer capable (1: enable. 0 disable) + Bit 18-16: Number of sounding dimensions (set to maximum-1 + if Bit 11 is 1. Otherwise, reserved and set to 0) + Bit 15-13: Compressed steering number of beamformer + antennas supported (set to maximum-1 if Bit 12 is 1. + Otherwise, reserved and set to 0) + Bit 12: SU beamformee capable (1: enable. 0 disable) + Bit 11: SU beamformer capable (1: enable. 0 disable) + Bit 10-8: Rx STBC + 0: no support + 1: support of 1 spatial stream + 2: support of 1-2 streams + 3: support of 1-3 spatial streams + 4: support of 1-4 spatial streams + 5-7: reserved and set to 0 + Bit 7: TX STBC (1: enable. 0 disable) + Bit 6: Short GI for 160 and 80+80 MHz (1: enable. 0 disable) + Bit 5: Short GI for 80 MHz (1: enable. 0 disable) + Bit 4: Rx LDPC (1: enable. 0 disable) + Bit 3-2: Supported channel width set. + 0: no support of either 160 or 80+80 MHz. + 1: support of 160 MHz + 2: support of both 160 and 80+80 MHz. + 3: reserved and set to 0. + Bit 1-0: Maximum MPDU length + 0: 3895 octets. + 1: 7991 octets. + 2: 11454 octets. + 3: reserved and set to 0. + + Note: for the STA, if <txrx> is 1 (Tx operations), the bitmap for <vhtcap> may be simplied as follows: + Bit 31-8: Reserved and set to 0 + Bit 7: Tx STBC (1: enable. 0 disable) + Bit 6: Reserved and set to 0 + Bit 5: Short GI for 80 Mhz (1: enable. 0 disable) + Bit 4: LDPC (1: enable. 0 disable) + Bit 3-0: Reserved and set to 0 + + where [n] is <tx_mcs_map>, + <tx_mcs_map> - This parameter specifies the TX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations). + It is a bitmap and should be used as following + Bit 15-0: MCS map, which is defined as folows: + Bit 15-14: Max MCS for 8 SS + Bit 13-12: Max MCS for 7 SS + Bit 11-10: Max MCS for 6 SS + Bit 9-8: Max MCS for 5 SS + Bit 7-6: Max MCS for 4 SS + Bit 5-4: Max MCS for 3 SS + Bit 3-2: Max MCS for 2 SS + Bit 1-0: Max MCS for 1 SS + + where [o] is <rx_mcs_map>. + <rx_mcs_map> - This parameter specifies the RX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations). + It is a bitmap with the same sructure as for <tx_mcs_map> + rx_mcs_map = 0xffff : FW will disable 802.11ac + rx_mcs_map = others : FW will enable 802.11ac + + Note: The user setting of vhtcap may be overwritten by the driver + if the setting of those fields is beyond the hardware capabilities. + + Examples: + mlanutl mlan0 vhtcfg 2 1 : Get current VHT configuration in 5GHz for the STA. + mlanutl mlan0 vhtcfg 2 2 : Get maximum VHT configuration in 5GHz for the STA. + mlanutl mlan0 vhtcfg 2 1 1 0x000001f0 + : Set the Tx operations configuration in 5GHz for the STA, + Tx BW follows the VHT Capabilities. + mlanutl mlan0 vhtcfg 2 2 0 0x000001f0 0xfff5 0xfffa + : Set the VHT capabilities configuration in 5GHz for the STA, + the Tx supports MCS 0-8 for both 1 and 2 spatial streams, + while the Rx supports MCS 0-9 for both 1 and 2 spatial streams. + mlanutl uap0 vhtcfg 2 3 0 0x000001f0 0xfffa 0xfffa + : Set the current/maximum VHT configuration in 5GHz for the uAP. + Both Tx and Rx supports MCS 0-9 for both 1 and 2 spatial streams. + mlanutl uap0 vhtcfg 2 3 0 0x000001b0 + : Set the VHT capability information in 5GHz for the uAP, and keep the Tx/Rx MCS Map same as before. + +opermodecfg + This command is used to set and get 11ac Operating Mode Notification configuration. + + where <m> is <bw> + <bw> - This is the channel width setting for the opermodecfg + 1: 20MHz + 2: 40MHz + 3: 80MHz + 4: 160MHz or 80+80MHz + + where <n> is <nss> + <nss> - This parameter specifies the nss that the STA can receive. + 1: NSS1 + 2: NSS2 + 3: NSS3 + 4: NSS4 + 5: NSS5 + 6: NSS6 + 7: NSS7 + 8: NSS8 + +wakeupreason + This command is used to get the host sleep wakeup reason. + + Usage: + mlanutl mlanX wakeupreason + mlanutl uapX wakeupreason + Examples: + mlanutl mlan0 wakeupreason : Get the host sleep wakeup reason + mlanutl uap0 wakeupreason : Get the host sleep wakeup reason + 0: unknown + 1: Broadcast data matched + 2: Multicast data matched + 3: Unicast data matched + 4: Maskable event matched + 5. Non-maskable event matched + 6: Non-maskable condition matched (EAPoL rekey) + 7: Magic pattern matched + 8: Control frame matched + 9: Management frame matched + Others: reserved. (0) + +warmreset + This command is used for warm reset of the interface. + + Usage: + mlanutl mlanX warmreset + +wpssession + This command is used to control wps session. No argument is used to get. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + mlanutl mlan0 wpssession 1 : Enable wpssession + mlanutl mlan0 wpssession : Get wpssession status + +wmmcfg + This command is used to control WMM. No argument is used to get. + + where value of n is: + 0 -- Disable + 1 -- Enable + + Examples: + mlanutl mlan0 wmmcfg 1 : Enable WMM + mlanutl mlan0 wmmcfg : Get WMM status + +wmmparamcfg + This command is used to configure WMM paramameters. + + Usage: + mlanutl mlanX wmmparamcfg [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP] + + The supported option are: + AC_BE: 0 + AC_BK: 1 + AC_VI: 2 + AC_V0: 3 + AIFSN: AIFSN value + ECW_MAX: ECW max + ECW_MIN: ECW min + TX_OP: TXOP Limit + empty - Get current WMM parameters + + Example: + mlanutl mlanX wmmparamcfg 0 3 10 4 0 + Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + + mlanutl mlanX wmmparamcfg 1 7 10 4 0 + Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + + mlanutl mlanX wmmparamcfg 2 2 4 3 94 + Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94 + + mlanutl mlanX wmmparamcfg 3 2 3 2 47 + Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47 + + mlanutl mlanX wmmparamcfg + Get current WMM parameters + + mlanutl mlanX wmmparamcfg 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47 + Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94 + Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47 + +wwscfg + This command is used to set/get the WWS (World Wide Safe) mode. + + where value of m is: + 0 -- Disable WWS mode (default) + 1 -- Enable WWS mode + + Examples: + mlanutl mlan0 wwscfg : Get WWS mode + mlanutl mlan0 wwscfg 1 : Enable WWS mode + mlanutl mlan0 wwscfg 0 : Disable WWS mode + +mc_cfg + This command is used to set/get the channel time. + + Usage: + mlanutl mlanX mc_cfg [n] + + where <n> : Channel time in microseconds. + + Examples: + mlanutl mlanX mc_cfg : Get Channel time and buffer weight. + mlanutl mlanX mc_cfg 10000 : Set Channel time to 10000us. + +mc_policy + This command is used to set/get the multi-channel policy. + Note: This is a device specific command. Hence, setting on one interface is + reflected on all other interfaces. + + Usage: + mlanutl mlanX mc_policy [n] + + where <n> : Multi-channel policy + + Examples: + mlanutl mlanX mc_policy : Get multi-channel policy setting. + mlanutl mlanX mc_policy 1 : Set multi-channel policy to 1. + mlanutl mlanX mc_policy 0 : Disable multi-channel policy + +mc_cfg_ext + This command is used to set/get the drcs parameters. + + Usage: + mlanutl mlanX mc_cfg_ext [c] [s] [u] [m] <a> <b> <d> <e> + + where: + channel index0: + [c] : chantime(in TU) + [s] : switchtime(in TU) + [u] : undozetime(in TU) + [m] : mode :0x0 --- PM1(default) + 0x1 --- Null2Self + channel index1: + <a> : chantime(in TU) + <b> : switchtime(in TU) + <d> : undozetime(in TU) + <e> : mode :0x0 --- PM1(default) + 0x1 --- Null2Self + Note: + channel index0: the first channel + channel index1: the second channel + undozetime should be less than other channel's switchtime + If want to set two channels the same parameters, just ignore the last four parameters and + use [c] [s] [u] [m] to set. + Examples: + mlanutl mlanX mc_cfg_ext : Get the drcs parameters for two channels. + mlanutl mlanX mc_cfg_ext 15 10 5 0 : Set two channels:channeltime 15TU, switchtime 10TU, undozetime 5TU, mode PM1 + mlanutl mlanX mc_cfg_ext 15 10 5 0 25 15 9 0 : Set channel index0: channeltime 17TU, switchtime 10TU, undozetime 5TU, mode PM1; + set channel index1: channeltime 25TU, switchtime 15TU, undozetime 9TU, mode PM1. + +cfg_noa + This is used to get/set P2P NoA (Notice of Absence) parameters only for P2P GO. + + Usage: + mlanutl p2pX cfg_noa [h] [i] [j] [k] [l] + + where: + [h] : noa_enable : 1/0 Set to 1 to enable NoA, 0 to disable NoA. + [i] : index : 0 - 255 Identifies an instance of NoA timing. + [j] : count : 1 - 255 Indicates the number of absence intervals. + 255 means a continuous schedule. + [k] : duration : Indicates the maximum duration in units of microseconds + that P2P GO can remain absent following the start of + a NoA interval. + [l] : interval : Indicates the length of the NoA interval in units of + microseconds. + + Examples: + mlanutl p2pX cfg_noa : Get noa configuration. + mlanutl p2pX cfg_noa 1 1 255 50 100 : Set noa configuration. + +cfg_opp_ps + This is used to get/set P2P OPP-PS parameters only for P2P GO. + + Usage: + mlanutl p2pX cfg_opp_ps [m] [n] + + where: + [m] : ps_enable : 1/0 Set to 1 to indicate P2P GO is using opportunistic + power save. Set to 0 if opportunistic power save is disabled. + [n] : ct_window : A period of time in TU after a TBTT during which P2P GO + is present. 0 indicates that there shall be no + CTWindow (Client Traffic Window). + + Examples: + mlanutl p2pX cfg_opp_ps : Get noa configuration. + mlanutl p2pX cfg_opp_ps 1 7 : Set noa configuration. + +rxpktcoal_cfg + This is used to get/set RX packet coalescing paramters + Usage: + mlanutl mlanX rxpktcoal_cfg [m] [n] + + where: + [m]: pkt_threshold: count after which packets would be sent to host. Valid values 1-7 + [n]: delay: timeout in ms after which packets would be sent to host. Valid values 1-4 + Coalescing is disabled if both or either of packet_thershold and delay is zero + + RX packet coalescing parameters can be changed only when device is in + idle state i.e. all interfaces are disconnected. + +get_sensor_temp + This command is used to get SOC temperature + Usage: + mlanutl mlanX get_sensor_temp + +indrstcfg + This command is used to set/ get independent reset mode configuration + + Usage : + mlanutl <interface> indrstcfg <ir_mode> [gpio_pin] + + interface : mlanX, uapX + ir_mode : 0 -- Disable + 1 -- Enable out band reset, disable in band + 2 -- Enable in band, disable out band + gpio_pin : 255 -- Default pin for reset + any other number for changing the gpio for reset. + + Example : + mlanutl mlan0 indrstcfg 1 255 : Set default pin on interface mlan0 as reset pin + mlanutl mlan0 indrstcfg 0 : Disable the gpio 17 as reset pin on interface mlan0 + mlanutl mlan0 indrstcfg : Get the status and the pin used for reset pin + mlanutl mlan0 indrstcfg 2 : Enable in band reset mode + + This command is used to set FW wakeup method and GPIO pin + Usage : + mlanutl <interface> fwwakeupmethod <method> <GPIO_pin> + interface : mlanX + method: + 1 -- Firmware wakeup through the interface command interrupt + -- (default setting for SDIO/PCIe/USB) + 2 -- Firmware wakeup through the GPIO pin + GPIO_pin: If firware wakeup throug GPIO pin, [g] is GPIO pin number + Example : + mlanutl mlan0 fwwakeupmethod : Get current wakeup method + mlanutl mlan0 fwwakeupmethod 1 : Set wakeup method is interface method + mlanutl mlan0 fwwakeupmethod 2 5 : Set wakeup method is GPIO method and GPIO pin is 5. + +robustcoex + This command is used to set robust coex. + + Usage : + mlanutl <interface> robustcoex <gpiocfg> [Enable/Disable] [gpionum] [gpiopolarity] + Enable/Disable : 0 -- Disable ; 1 -- Enable + gpionum : Number of gpio + gpiopolarity : polarity of gpio + + Example : + mlanutl mlan0 robustcoex gpiocfg 1 4 1 : Enable robustcoex gpio, set gpionum to 4 and gpiopolarity to 1 + mlanutl mlan0 robustcoex gpiocfg 0 : Disable robustcoex gpio + +keep_connect + This command is used to refuse disconnect + + Usage : + mlanutl <interface> keep_connect [Enable/Disable] + Enable/Disable : 0 -- Disable ; 1 -- Enable + + Example : + mlanutl mlan0 keep_connect 1 : Enable keep connect + mlanutl mlan0 keep_connect 0 : Disable keep connect +ctrldeauth + This command is used to set/get firmware ctrldeauth setting + Usage : + mlanutl uapX ctrldeauth <n> + + Where value of n is : + 0 -- Firmware will use default behavior + 1 -- Firmware will not send deauth packet when uap move to another channel. + + Example : + mlanutl uap0 ctrldeauth : Get current setting + mlanutl uap0 ctrldeauth : Firmware will not send deauth packet when uap move to different channel. + +bootsleep + This command is used to set and get boot sleep configure. + + Usage : + mlanutl mlanX/uapX bootsleep <enable> + <enable> : enable boot sleep + : 0 - disable boot sleep + : 1 - enable boot sleep + + Example : + mlanutl mlan0/uap0 bootsleep 1 : Enable boot sleep + mlanutl mlan0/uap0 bootsleep : Get boot sleep configure + + +===============================================================================
diff --git a/wlan_sd8897/README_OPENWRT b/wlan_sd8897/README_OPENWRT new file mode 100644 index 0000000..90a6722 --- /dev/null +++ b/wlan_sd8897/README_OPENWRT
@@ -0,0 +1,356 @@ +================================================================================= + U S E R M A N U A L F O R OpenWrt + +This section describes detailed steps to add Marvell Wireless NIC (sdio/usb/pcie) +driver to OpenWrt build system. + +Add marvell driver (sdio/usb/pcie) to OpenWrt build system. + +1. Go to the openwrt source code folder +2. make menuconfig + choose x86 platform + other general openwrt configurations please refer to: + https://wiki.openwrt.org/doc/howto/build +3. After make menuconfig + make sure that following configurations are set in .config + CONFIG_PACKAGE_hostapd=y + CONFIG_PACKAGE_hostapd-common=y + CONFIG_PACKAGE_kmod-cfg80211=y +4. Go to the openwrt source code folder and Compile openwrt BSP + make V=s [-j[number]] +5. After the first time compiling, openwrt/dl folder will be created + , related packages will be downloaded during compiling, so you need + network access as long as you need to compile openwrt BSP. +6. Go to the openwrt source code folder: openwrt/dl/ +7. Find compat-wireless-xxxx-xx-xx.tar.bz2, and uncompress the package +8. Go to compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/ +9. Modify compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/Kconfig + + source "drivers/net/wireless/marvell/libertas/Kconfig" + source "drivers/net/wireless/marvell/libertas_tf/Kconfig" + source "drivers/net/wireless/marvell/mwifiex/Kconfig" + +source "drivers/net/wireless/marvell/mrvl-pcie/Kconfig" + +source "drivers/net/wireless/marvell/mrvl-sd8xxx/Kconfig" + +source "drivers/net/wireless/marvell/mrvl-usb/Kconfig" + +10. Modify compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/Makefile + + obj-$(CPTCFG_LIBERTAS_THINFIRM) += libertas_tf/ + obj-$(CPTCFG_MWIFIEX) += mwifiex/ + +obj-$(CPTCFG_MRVL_PCIE) += mrvl-pcie/ + +obj-$(CPTCFG_MRVL_SD8XXX) += mrvl-sd8xxx/ + +obj-$(CPTCFG_MRVL_USB) += mrvl-usb/ + +11. Go to compat-wireless-xxxx-xx-xx/drivers/net/wireless/marvell/ +12. mkdir (mrvl-pcie/mrvl-sd8xxx/mrvl-usb) +13. copy (pcie/sdio/usb)/wlan_src/* to (mrvl-pcie/mrvl-sd8xxx/mrvl-usb) +14. Go to mrvl-pcie/mrvl-sd8xxx/mrvl-usb +15. Add new Kconfig: + mrvl-pcie/Kconfig + + +config MRVL_PCIE + + tristate "Marvell Wireless Driver for PCIE 8997" + + depends on m + + depends on PCI + + depends on CFG80211 + + depends on WIRELESS_EXT + + ---help--- + + This adds support for wireless adapters based on Marvell + + pcie 8997 chipsets with PCIe interface. + + + + If you choose to build it as a module, it will be called + + pcie8xxx. + + mrvl-sd8xxx/Kconfig + + +config MRVL_SD8XXX + + tristate "Marvell sdio 802.11n/802.11ac Wireless cards" + + depends on m + + depends on MMC + + depends on CFG80211 + + depends on WIRELESS_EXT + + ---help--- + + A driver for Marvell sdio 802.11n/802.11ac Wireless cards. + + mrvl-usb/Kconfig + + +config MRVL_USB + + tristate "Marvell sdio 802.11n/802.11ac Wireless cards" + + depends on m + + depends on USB + + depends on CFG80211 + + depends on WIRELESS_EXT + + ---help--- + + A driver for Marvell sdio 802.11n/802.11ac Wireless cards. + +16. Open moal_main.h under (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlinux/ + + find #define COMPAT_VERSION_CODE KERNEL_VERSION_CODE(x, x, x) + modify (x, x, x) to the kernel version code of backports/compat-wireless package + + backported kernel version code could be found in + compat-wireless-xxxx-xx-xx/version as following + + 'BACKPORTED_KERNEL_VERSION="v4.4-rc5-1913-gc8fdf68"' + v4.4 means KERNEL_VERSION(4, 4, 0) + +17. Modify (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/Makefile + + # OpenWrt support + -CONFIG_OPENWRT_SUPPORT=n + +CONFIG_OPENWRT_SUPPORT=y + + ... + + ############################################################################# + # Select Platform Tools + ############################################################################# + + MODEXT = ko + -ccflags-y += -I$(M)/mlan + +ccflags-y += -I$(M)/drivers/net/wireless/marvell/(mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlan + ccflags-y += -DLINUX + + + ifeq ($(CONFIG_EMBEDDED_SUPP_AUTH), y) + -ccflags-y += -I$(M)/mlan/esa + -ccflags-y += -I$(M)/mlan/esa/common + +ccflags-y += -I$(M)/drivers/net/wireless/marvell/(mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlan/esa + +ccflags-y += -I$(M)/drivers/net/wireless/marvell/(mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/mlan/esa/common + endif + +18. compress compat-wireless-xxxx-xx-xx to its original .tar.bz2 format +19. Go to the openwrt source code folder: openwrt/package/kernel/mac80211/ +20. Modify openwrt/package/kernel/mac80211/Makefile. + + PKG_DRIVERS = \ + adm8211 \ + ath ath5k ath9k ath9k-common ath9k-htc ath10k \ + b43 b43legacy \ + carl9170 \ + hermes hermes-pci hermes-pcmcia hermes-plx\ + iwl-legacy iwl3945 iwl4965 iwlwifi \ + lib80211 \ + libipw ipw2100 ipw2200 \ + libertas-sdio libertas-usb libertas-spi \ + mac80211-hwsim \ + mt7601u \ + mwl8k mwifiex-pcie \ + +mrvl-pcie \ + +mrvl-sd8xxx \ + +mrvl-usb \ + p54-common p54-pci p54-spi p54-usb \ + rt2x00-lib rt2x00-pci rt2x00-usb \ + rt2400-pci rt2500-pci rt2500-usb \ + + ... + + +define KernelPackage/mrvl-pcie + + $(call KernelPackage/mac80211/Default) + + TITLE:=Marvell pcie wireless driver + + URL:=http://wireless.kernel.org/en/users/Drivers/mwifiex + + DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +@DRIVER_11N_SUPPORT + + FILES:= \ + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-pcie/mlan.ko \ + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-pcie/pcie8xxx.ko + + AUTOLOAD:=$(call AutoProbe,mrvl-pcie) + +endef + + +define KernelPackage/mrvl-pcie/description + + Kernel modules for Marvell pcie 802.11n/802.11ac PCIe Wireless cards + +endef + + +define KernelPackage/mrvl-sd8xxx + + $(call KernelPackage/mac80211/Default) + + DEPENDS+= +kmod-cfg80211 +kmod-lib80211 +kmod-mmc +@DRIVER_WEXT_SUPPORT + + TITLE:=Marvell sdio Wireless Driver + + FILES:= \ + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-sd8xxx/mlan.ko \ + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-sd8xxx/sd8xxx.ko + + AUTOLOAD:=$(call AutoProbe, mrvl-sd8xxx) + +endef + + +define KernelPackage/mrvl-sd8xxx/description + + Kernel modules for Marvell sdio 802.11n/802.11ac Wireless cards + +endef + + +define KernelPackage/mrvl-usb + + $(call KernelPackage/mac80211/Default) + + DEPENDS+= @USB_SUPPORT +kmod-cfg80211 +kmod-usb-core +kmod-lib80211 +@DRIVER_WEXT_SUPPORT + + TITLE:=Marvell usb Wireless Driver + + FILES:= \ + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-usb/mlan.ko \ + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-usb/usb8xxx.ko + + AUTOLOAD:=$(call AutoProbe, mrvl-usb) + +endef + + +define KernelPackage/mrvl-usb/description + + Kernel modules for Marvell usb 802.11n/802.11ac Wireless cards + +endef + + ... + + config-$(call config_package,mwl8k) += MWL8K + config-$(call config_package,mwifiex-pcie) += MWIFIEX MWIFIEX_PCIE + +config-$(call config_package,mrvl-pcie) += MRVL_PCIE + +config-$(call config_package,mrvl-sd8xxx) += MRVL_SD8XXX + +config-$(call config_package,mrvl-usb) += MRVL_USB + config-$(call config_package,rtl8180) += RTL8180 + config-$(call config_package,rtl8187) += RTL8187 + + ... + + $(eval $(call KernelPackage,mwl8k)) + $(eval $(call KernelPackage,mwifiex-pcie)) + +$(eval $(call KernelPackage,mrvl-pcie)) + +$(eval $(call KernelPackage,mrvl-sd8xxx)) + +$(eval $(call KernelPackage,mrvl-usb)) + $(eval $(call KernelPackage,p54-common)) + +OpenWrt configuration +21. Go to OpenWrt srouce code folder +22. Make menuconfig + + select one of the newly added marvell driver + │-> Kernel modules + │ -> Wireless Drivers + | ->mrvl-pcie + | ->mrvl-sd8xxx + | ->mrvl-usb + +23. Other general configurations, please refer to the https://wiki.openwrt.org/doc/howto/build +24. after menucofig make soure that following configurations are seletect + + CONFIG_PACKAGE_hostapd=y + CONFIG_PACKAGE_hostapd-common=y + CONFIG_PACKAGE_kmod-cfg80211=y + + Go to openwrt folder and re-build openwrt BSP + make V=s [-j[number]] + + After openwrt BSP is successfully build, kernel modules could be found + both in the build folder and the running OS once openwrt is boot up. + kernel modules will be written to u-disk while openwrt img is burned to u-disk. + + kernel modules in openwrt build dir: + openwrt/build_dir/target-x86_64_uClibc-0.9.33.2/linux-x86_64/compat-wireless-2016-01-10/ + ipkg-x86_64/kmod-mrvl-(usb/sd8xxx/pcie)/lib/modules/3.18.45/ + + kernel modules in openwrt running OS: + /lib/modules/xx.xx.xx/ + +25. To support SDIO card in OpenWrt x86 platforma + Add following kernel configurations for OpenWrt + Go to OpenWrt source code foler + + make kernel_menuconfig + + Device Drivers----> + --- MMC/SD/SDIO card support + [ ] MMC debugging + [ ] MMC host clock gating + *** MMC/SD/SDIO Card Drivers *** + < > MMC block device driver + < > SDIO UART/GPS class support + < > MMC host test driver + *** MMC/SD/SDIO Host Controller Drivers *** + <*> Secure Digital Host Controller Interface support + <*> SDHCI support on PCI bus + [ ] Ricoh MMC Controller Disabler + < > SDHCI platform and OF driver helper + < > Winbond W83L51xD SD/MMC Card Interface support + < > TI Flash Media MMC/SD Interface support + < > ENE CB710 MMC/SD Interface support + < > VIA SD/MMC Card Reader Driver + < > Renesas USDHI6ROL0 SD/SDIO Host Controller support + + Compile OpenWrt using Make V=s, then write openwrt img to u-disk and boot up + +26. Bring up marvell wireless NIC on OpenWrt system + + copy pcie/sdio/usb firmware to /lib/firmware/mrvl folder + On OpenWrt running OS + scp usrname@<target_pc ip address>:/path/firmware.bin /lib/firmware/mrvl + + Another way is to add firmware binary to OpenWrt source code + +27. load marvell pcie/sdio/usb driver modules + + End with Marvell Wirelss NIC card bring up on OpenWrt + +======================================================================================================= +If firmware is copied to OpenWrt via scp, following steps could be ignored. +28. Add firmware binary in OpenWrt + Copy pcie/usb/sdio firmware to folder: + openwrt/build_dir/target-x86_64_uClibc-0.9.33.2/linux-firmware-52442afee9907bc32a058f22bb3295d040677c26/mrvl/ +29. Go to penwrt/package/firmware/linux-firmware + Modify marvell.mk as following + + +Package/mrvl-pcie-firmware = $(call Package/firmware-default,Marvell PCIE8997 firmware) + +define Package/mrvl-pcie-firmware/install + + $(INSTALL_DIR) $(1)/lib/firmware/mrvl + + $(INSTALL_DATA) \ + + $(PKG_BUILD_DIR)/mrvl/pcieusb8997_combo_v4.bin \ + + $(1)/lib/firmware/mrvl/ + +endef + +$(eval $(call BuildPackage,mrvl-pcie-firmware)) + + +Package/mrvl-sd8xxx-firmware = $(call Package/firmware-default,Marvell SDIO8997 firmware) + +define Package/mrvl-sd8xxx-firmware/install + + $(INSTALL_DIR) $(1)/lib/firmware/mrvl + + $(INSTALL_DATA) \ + + $(PKG_BUILD_DIR)/mrvl/sdsd8997_combo_v4.bin \ + + $(1)/lib/firmware/mrvl/ + +endef + +$(eval $(call BuildPackage,mrvl-sd8xxx-firmware)) + + +Package/mrvl-usb-firmware = $(call Package/firmware-default,Marvell SDIO8997 firmware) + +define Package/mrvl-usb-firmware/install + + $(INSTALL_DIR) $(1)/lib/firmware/mrvl + + $(INSTALL_DATA) \ + + $(PKG_BUILD_DIR)/mrvl/usbusb8997_combo_v4.bin \ + + $(1)/lib/firmware/mrvl/ + +endef + +$(eval $(call BuildPackage,mrvl-usb-firmware)) + +30. re-modify openwrt/package/kernel/mac80211/Makefile + + for PCIE + + DEPENDS+= @PCI_SUPPORT +kmod-mac80211 +@DRIVER_11N_SUPPORT +mrvl-pcie-firmware + for SDIO + + DEPENDS+= +kmod-cfg80211 +kmod-lib80211 +kmod-mmc +@DRIVER_WEXT_SUPPORT +mrvl-sd8xxx-firmware + for USB + + DEPENDS+= @USB_SUPPORT +kmod-cfg80211 +kmod-usb-core +kmod-lib80211 +@DRIVER_WEXT_SUPPORT +mrvl-usb-firmware +31. Go to step 25. +======================================================================================================= +To support SDIO, USB and PCIE in one OpenWrt BSP +if no need to support multi bus types in one BSP, ignore the following steps +32. Re-modify Modify openwrt/package/kernel/mac80211/Makefile. + for PCIE + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-pcie/mlan-pcie.ko \ + for SDIO + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-sd8xxx/mlan-sdio.ko \ + for USB + + $(PKG_BUILD_DIR)/drivers/net/wireless/marvell/mrvl-usb/mlan-usb.ko \ +33. Re-modify (mrvl-pcie/mrvl-sd8xxx/mrvl-usb)/Makefile + for PCIE + + -obj-m := mlan.o + -mlan-objs := $(MLANOBJS) + +obj-m := mlan-pcie.o + +mlan-pcie-objs := $(MLANOBJS) + + for SDIO + + -obj-m := mlan.o + -mlan-objs := $(MLANOBJS) + +obj-m := mlan-sdio.o + +mlan-sdio-objs := $(MLANOBJS) + + for USB + + -obj-m := mlan.o + -mlan-objs := $(MLANOBJS) + +obj-m := mlan-usb.o + +mlan-usb-objs := $(MLANOBJS) +34. Go to step 25.
diff --git a/wlan_sd8897/README_RBC b/wlan_sd8897/README_RBC new file mode 100644 index 0000000..c849287 --- /dev/null +++ b/wlan_sd8897/README_RBC
@@ -0,0 +1,103 @@ +=============================================================================== + U S E R M A N U A L for Robust BT-WLAN coex (RBC) + + Copyright (C) 2014-2018, Marvell International Ltd. + All Rights Reserved +=============================================================================== + +############################### + +# Abbreviations and acronyms + +############################### +RBC - Robust BT-WLAN co-existence +TMD - Time Distribute +SMPS - Spatial Multiplexing Power Save + +############################### + +# Default RBC modes: + +############################### +1. For systems where BT and WLAN have seperate antennas, RBC mode is + automatically disabled in FW after detecting this from FEM cal data. e.g. + For 1x1 Dual-Ant system and 2x2 3-Ant system RBC mode is disabled. +2. For systems where BT and WLAN share an antenna, RBC mode is automatically + enabled in FW after detecting this from FEM cal data. + Default modes: + 1x1 single antenna system: TMD RBC + 2x2 2-antenna system: 1x1 SMPS RBC + + +################################# + +# 2x2 2-antenna system RBC modes + +################################# +There are three mutually exclusive RBC modes for a 2x2 system. +The default RBC mode is 1x1 SMPS RBC. The required RBC mode must be +configured before starting uAP or associating in-STA. The mode cannot be +changed dynamically when any of these connections is active. The modes are +described below: +1. 1x1 SMPS RBC mode: WLAN switches to 1x1 FEM when BT is turned on. Also, + in-STA goes to SMPS mode w.r.t. ext-AP. When BT is turned off, WLAN + switches back to 2x2 FEM setting and in-STA moves out of SMPS. + uAP starts bss with only 1-stream even BT is off because it cannot + dynamically move between 1-stream and 2-stream rates like in-STA. To start + uAP with 2-stream, RBC mode has to be disabled. +2. 1x2 SMPS RBC mode: Similar as 1x1 SMPS RBC mode. WLAN switches to 1x2 FEM + when BT is turned on. In this mode, it is expected that when BT is not + actively transmitting, WLAN can receive on both the antennas to enhance + the range. Note that 1-stream rates are used for receive and transmit. +3. 2x2 TMD RBC mode: WLAN uses 2x2 antenna setting and timeshares the antenna + with BT. + +############################### + +# RBC mode select: + +############################### + +User can use robust_btc.conf and hostcmd to select different RBC mode: + +hostcmd mode_get +hostcmd mode_timeshare +hostcmd mode_spatial + This command is used to get/set Robust BT Coex. + mode_get: get the current mode + mode_timeshare: set Robust BT Coex to timeshare mode (default on 1x1 chips) + mode_spatial: set Robust BT Coex to spatial mode (only for, and default on 2x2 chips) + + Usage: + mlanconfig mlanX hostcmd config/robust_btc.conf mode_get + mlanconfig mlanX hostcmd config/robust_btc.conf mode_timeshare + mlanconfig mlanX hostcmd config/robust_btc.conf mode_spatial + +hostcmd gpio_cfg + This command is used to enable/disable GPIO cfg. + gpio_cfg: enable/disable GPIO cfg for external bt request (default is enable with High Polarity) + + Usage: + mlanconfig mlanX hostcmd config/robust_btc.conf gpio_cfg + +hostcmd generictime +hostcmd a2dptime +hostcmd inquirytime +hostcmd ap_generictime +hostcmd ap_a2dptime +hostcmd ap_inquirytime + This command is used to configure the time slice of COEX (only works in timeshare mode) + generictime: configure the Bttime and Wlantime in Station Generic case + a2dptime: configure the Bttime and Wlantime in Station A2DP case + inquirytime: configure the Bttime and Wlantime in Station Inquiry case + ap_generictime: configure the Bttime and Wlantime in Ap Generic case + ap_a2dptime: configure the Bttime and Wlantime in Ap A2DP case + ap_inquirytime: configure the Bttime and Wlantime in Ap Inquiry case + + Usage: + mlanutl mlanX hostcmd config/robust_btc.conf generictime + mlanutl mlanX hostcmd config/robust_btc.conf a2dptime + mlanutl mlanX hostcmd config/robust_btc.conf inquirytim + mlanutl mlanX hostcmd config/robust_btc.conf ap_generictime + mlanutl mlanX hostcmd config/robust_btc.conf ap_a2dptime + mlanutl mlanX hostcmd config/robust_btc.conf ap_inquirytime
diff --git a/wlan_sd8897/README_UAP b/wlan_sd8897/README_UAP new file mode 100644 index 0000000..b05fe8a --- /dev/null +++ b/wlan_sd8897/README_UAP
@@ -0,0 +1,2715 @@ +=============================================================================== + U S E R M A N U A L + + Copyright (C) 2009-2018, Marvell International Ltd. + All Rights Reserved + +1) FOR DRIVER BUILD + + Goto source code directory wlan_src. + make [clean] build + The driver binaries can be found in ../bin_xxxx directory. + The driver code supports Linux kernel up to 4.14. + +2) FOR DRIVER INSTALL + + a) Copy sd8786_uapsta.bin | sd8787_uapsta.bin | ... to /lib/firmware/mrvl/ directory, + create the directory if it doesn't exist. + b) Install uAP driver, + There are drv_mode, max_sta_bss, max_uap_bss etc. module parameters. + The bit settings of drv_mode are, + Bit 0 : STA + Bit 1 : uAP + Bit 2 : WIFIDIRECT + The default drv_mode is 7. + Bit 4 : NAN + + max_uap_bss: Maximum number of uAP BSS (default 1, max 2) + uap_name: Name of the uAP interface (default: "uap") + For example, to install SD8787 driver, + To load driver in uAP only mode, + insmod mlan.ko + insmod sd8787.ko drv_mode=2 [fw_name=mrvl/sd8787_uapsta.bin] + To switch mode between STA only, uAP only and uAPSTA in run time, + echo drv_mode=1 > /proc/mwlan/config // STA mode + echo drv_mode=2 > /proc/mwlan/config // uAP mode + echo drv_mode=3 > /proc/mwlan/config // uAPSTA mode + c) Uninstall uAP driver, + ifconfig uapX down + rmmod sd8xxx + rmmod mlan + + To load driver with MFG firmware file, use mfg_mode=1 when insmod WLAN driver and + specify MFG firmware name if needed. + + There are some other parameters for debugging purpose etc. Use modinfo to check details. + drvdbg=<bit mask of driver debug message control> + dev_cap_mask=<Bit mask of the device capability> + mac_addr=xx:xx:xx:xx:xx:xx <override the MAC address (in hex)> + auto_ds=0|1|2 <use MLAN default | enable auto deepsleep | disable auto deepsleep> + ps_mode=0|1|2 <use MLAN default | enable IEEE PS mode | disable IEEE PS mode> + max_tx_buf=2048|4096|8192 <maximum AMSDU Tx buffer size> + pm_keep_power=1|0 <PM keep power in suspend (default) | PM no power in suspend> + shutdown_hs=1|0 <Enable HS when shutdown | No HS when shutdown (default)> + cfg_11d=0|1|2 <use MLAN default | enable 11d | disable 11d> + dts_enable=0|1 <Disable DTS | Enable DTS (default)> + hw_test=0|1 <Disable hardware test (default) | Enable hardware test> + fw_serial=0|1 <support parallel download FW | support serial download FW (default)> + req_fw_nowait=0|1 <use request_firmware API (default) | use request_firmware_nowait API> + init_cfg=<init config (MAC addresses, registers etc.) file name> + e.g. copy init_cfg.conf to firmware directory, init_cfg=mrvl/init_cfg.conf + cal_data_cfg=<CAL data config file name> + e.g. copy cal_data.conf to firmware directory, cal_data_cfg=mrvl/cal_data.conf + txpwrlimit_cfg=<Tx power limit config file name> + e.g. copy txpwrlimit_cfg_set.conf to firmware directory, txpwrlimit_cfg=mrvl/txpwrlimit_cfg_set.conf + init_hostcmd_cfg=<init hostcmd config file name> + e.g. copy init_hostcmd_cfg.conf to firmware directory, init_hostcmd_cfg=mrvl/init_hostcmd_cfg.conf + cfg80211_wext=<bit mask of CFG80211 and WEXT control> + Bit 0: STA WEXT + Bit 1: uAP WEXT + Bit 2: STA CFG80211 + Bit 3: uAP CFG80211 + wq_sched_prio: Priority for work queue + wq_sched_policy: Scheduling policy for work queue + (0: SCHED_NORMAL, 1: SCHED_FIFO, 2: SCHED_RR, 3: SCHED_BATCH, 5: SCHED_IDLE) + Please note that, both wq_sched_prio and wq_sched_policy should be provided + as module parameters. If wq_sched_policy is (0, 3 or 5), then wq_sched_prio + must be 0. wq_sched_prio should be 1 to 99 otherwise. + rx_work=0|1|2 <default | Enable rx_work_queue | Disable rx_work_queue> + low_power_mode_enable=0|1 <disable low power mode (default)| enable low power mode> + When low power mode is enabled, the output power will be clipped at ~+10dBm and the + expected PA current is expected to be in the 80-90 mA range for b/g/n modes + + Note: On some platforms (e.g. PXA910/920) double quotation marks ("") need to used + for module parameters. + insmod sd8xxx.ko "<para1> <para2> ..." + +3) FOR DRIVER PROC & DEBUG + The following info are provided in /proc/mwlan/uapX/info. + + driver_name = "uap" + driver_version = <driver version> + InterfaceName= "uapX" + State= "Disconnected" | "Connected" + MACAddress= <6-byte adapter MAC address> + MCCount= <multicast address count> + num_tx_bytes = <number of bytes sent to device> + num_rx_bytes = <number of bytes received from device and sent to kernel> + num_tx_pkts = <number of packets sent to device> + num_rx_pkts = <number of packets received from device and sent to kernel> + num_tx_pkts_dropped = <number of tx packets dropped by driver> + num_rx_pkts_dropped = <number of rx packets dropped by driver> + num_tx_pkts_err = <number of tx packets failed to send to device> + num_rx_pkts_err = <number of rx packets failed to receive from device> + num_tx_timeout = <number of tx timeout> + carrier "on" | "off" + tx queue "stopped" | "started" + + The following debug info are provided in /proc/mwlan/uapX/debug. + + drvdbg = <bit masks of driver debug message control> + bit 0: MMSG PRINTM(MMSG,...) + bit 1: MFATAL PRINTM(MFATAL,...) + bit 2: MERROR PRINTM(MERROR,...) + bit 3: MDATA PRINTM(MDATA,...) + bit 4: MCMND PRINTM(MCMND,...) + bit 5: MEVENT PRINTM(MEVENT,...) + bit 6: MINTR PRINTM(MINTR,...) + bit 7: MIOCTL PRINTM(MIOCTL,...) + ... + bit 16: MDAT_D PRINTM(MDAT_D,...), DBG_HEXDUMP(MDAT_D,...) + bit 17: MCMD_D PRINTM(MCMD_D,...), DBG_HEXDUMP(MCMD_D,...) + bit 18: MEVT_D PRINTM(MEVT_D,...), DBG_HEXDUMP(MEVT_D,...) + bit 19: MFW_D PRINTM(MFW_D,...), DBG_HEXDUMP(MFW_D,...) + bit 20: MIF_D PRINTM(MIF_D,...), DBG_HEXDUMP(MIF_D,...) + ... + bit 28: MENTRY PRINTM(MENTRY,...), ENTER(), LEAVE() + bit 29: MWARN PRINTM(MWARN,...) + bit 30: MINFO PRINTM(MINFO,...) + wmm_ac_vo = <number of packets sent to device from WMM AcVo queue> + wmm_ac_vi = <number of packets sent to device from WMM AcVi queue> + wmm_ac_be = <number of packets sent to device from WMM AcBE queue> + wmm_ac_bk = <number of packets sent to device from WMM AcBK queue> + max_tx_buf_size = <maximum Tx buffer size> + tx_buf_size = <current Tx buffer size> + curr_tx_buf_size = <current Tx buffer size in FW> + ps_mode = <0/1, CAM mode/PS mode> + ps_state = <0/1/2/3, awake state/pre-sleep state/sleep-confirm state/sleep state> + wakeup_dev_req = <0/1, wakeup device not required/required> + wakeup_tries = <wakeup device count, cleared when device awake> + hs_configured = <0/1, host sleep not configured/configured> + hs_activated = <0/1, extended host sleep not activated/activated> + tx_pkts_queued = <number of Tx packets queued> + num_bridge_pkts = <number of bridged packets> + num_drop_pkts = <number of dropped packets> + num_tx_timeout = <number of Tx timeout> + num_cmd_timeout = <number of timeout commands> + timeout_cmd_id = <command id of the last timeout command> + timeout_cmd_act = <command action of the last timeout command> + last_cmd_id = <command id of the last several commands sent to device> + last_cmd_act = <command action of the last several commands sent to device> + last_cmd_index = <0 based last command index> + last_cmd_resp_id = <command id of the last several command responses received from device> + last_cmd_resp_index = <0 based last command response index> + last_event = <event id of the last several events received from device> + last_event_index = <0 based last event index> + num_cmd_h2c_fail = <number of commands failed to send to device> + num_cmd_sleep_cfm_fail = <number of sleep confirm failed to send to device> + num_tx_h2c_fail = <number of data packets failed to send to device> + num_cmdevt_c2h_fail = <number of commands/events failed to receive from device> + num_rx_c2h_fail = <number of data packets failed to receive from device> + num_int_read_fail = <number of interrupt read failures> + last_int_status = <last interrupt status> + cmd_sent = <0/1, send command resources available/sending command to device> + data_sent = <0/1, send data resources available/sending data to device> + mp_rd_bitmap = <SDIO multi-port read bitmap> + curr_rd_port = <SDIO multi-port current read port> + mp_wr_bitmap = <SDIO multi-port write bitmap> + curr_wr_port = <SDIO multi-port current write port> + cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> + event_received = <0/1, no event to process/event received and yet to process> + ioctl_pending = <number of ioctl pending> + tx_pending = <number of Tx packet pending> + rx_pending = <number of Rx packet pending> + lock_count = <number of lock used> + malloc_count = <number of malloc done> + mbufalloc_count = <number of mlan_buffer allocated> + main_state = <current state of the main process> + sdiocmd53w = <SDIO Cmd53 write status> + sdiocmd53r = <SDIO Cmd52 read status> + hs_skip_count = <number of skipped suspends> + hs_force_count = <number of forced suspends> + + Example: + echo "drvdbg=0x7" > /proc/mwlan/uapX/debug #enable MMSG,MFATAL,MERROR messages + + Use dmesg or cat /var/log/debug to check driver debug messages. + + To log driver debug messages to file, + a) Edit /etc/rsyslog.conf, add one line "*.debug /var/log/debug" + b) touch /var/log/debug (if the file doesn't exist) + c) service rsyslog restart + +4) SOFT_RESET command + This command is used to perform a "soft reset" on the module. + The FW code will disable hardware and jump to boot code. + Host software will then need to re-download firmware if required. + + Usage: + echo "soft_reset=1" > /proc/mwlan/config + +=============================================================================== + + U S E R M A N U A L F O R UAPUTL + +NAME +uaputl.exe [options] <command> [command parameters]] + +Options: + --help Display help + -v Display version + -i <interface> + -d <debug_level=0|1|2> + +Example: + ./uaputl.exe --help + "display help for uaputl" + + ./uaputl.exe sys_config --help + "display help for sys_config command" + +This tool can be used to set/get uAP's settings. To change AP settings, you might +need to issue "bss_stop" command to stop AP before making change and issue "bss_start" +command to restart the AP after making change. + +------------------ +Supported Commands +------------------ +version +debug_level +sys_config [CONFIG_FILE_NAME] +bss_config [CONFIG_FILE_NAME] +sys_info +sys_reset +bss_start +bss_stop +sta_list +sta_deauth <STA_MAC_ADDRESS> +sta_deauth_ext <STA_MAC_ADDRESS> <REASON_CODE> +radioctrl [0|1] +txratecfg [l] [m] [n] +antcfg [m] [n] +pscfg [MODE] [CTRL INACTTO MIN_SLEEP MAX_SLEEP MIN_AWAKE MAX_AWAKE] +mic_err <STA_MAC_ADDRESS> +key_material <MAC_ADDRESS> <KEY> [KEY_ID] +sys_cfg_custom_ie [INDEX] [MASK] [IEBuffer] +coex_config [CONFIG_FILE_NAME] +hscfg [condition [[GPIO# [gap]]]] +hssetpara condition [[GPIO# [gap]]] +sys_cfg_wmm [qosinfo=<qosinfo>] + [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP] +sys_cfg_ap_wmm [0] + [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP] +sys_cfg_11n [ENABLE] [HTCAP] [AMPDU] [TXBFCAP] [HT_MCS_MAP] +addbapara [timeout txwinsize rxwinsize txamsdu rxamsdu] +aggrpriotbl <m0> <n0> <m1> <n1> ... <m7> <n7> +addbareject <m0> <m1> ... <m7> +httxbfcfg <ACTION> [ACT_DATA] +httxcfg [<m>] [<n>] +htstreamcfg [n] +deepsleep [MODE] [IDLE_TIME] +sdcmd52rw <FN no.> <address> [data] +hostcmd <txpwrlimit_cfg.conf> txpwrlimit_cfg_get +hostcmd <txpwrlimit_cfg.conf> txpwrlimit_2g_cfg_set +hostcmd <txpwrlimit_cfg.conf> txpwrlimit_5g_cfg_set +tx_data_pause [ENABLE][TX_BUF_CNT] +vhtcfg <j> <k> [l] [m] [n] [o] +dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>] +cscount [<channel_switch_count>] +mgmtframectrl [MASK] + +------------------------------------------------------------------- +The following commands can be issued individually for debug purpose +------------------------------------------------------------------- +sys_cfg_ap_mac_address [AP_MAC_ADDRESS] +sys_cfg_ssid [SSID] +sys_cfg_beacon_period [BEACON_PERIOD] +sys_cfg_dtim_period [DTIM_PERIOD] +sys_cfg_channel [CHANNEL] [MODE] +sys_cfg_channel_ext [CHANNEL] [BAND] [MODE] +sys_cfg_scan_channels [CHANNEL[.BAND]] +sys_cfg_rates [RATES] +sys_cfg_rates_ext [rates RATES] [mbrate RATE] +sys_cfg_tx_power [TX_POWER] +sys_cfg_bcast_ssid_ctl [0|1|2] +sys_cfg_preamble_ctl +sys_cfg_bss_status +sys_cfg_rts_threshold [RTS_THRESHOLD] +sys_cfg_frag_threshold [FRAG_THRESHOLD] +sys_cfg_rsn_replay_prot [1|0] +sys_cfg_tx_beacon_rate [TX_BEACON_RATE] +sys_cfg_mcbc_data_rate [MCBC_DATA_RATE] +sys_cfg_pkt_fwd_ctl [PKT_FWD_CTRL] +sys_cfg_sta_ageout_timer [STA_AGEOUT_TIMER] +sys_cfg_ps_sta_ageout_timer [PS_STA_AGEOUT_TIMER] +sys_cfg_auth [AUTH_MODE] +sys_cfg_protocol [PROTOCOL] [AKM_SUITE] +sys_cfg_pmf [MFPC] [MFPR] +sys_cfg_wep_key [INDEX ISDEFAULT KEY] +sys_cfg_cipher [PAIRWISE_CIPHER GROUP_CIPHER] +sys_cfg_pwk_cipher [<PROTOCOL>] [PAIRWISE_CIPHER] +sys_cfg_gwk_cipher [GROUP_CIPHER] +sys_cfg_group_rekey_timer [GROUP_REKEY_TIMER] +sys_cfg_wpa_passphrase [PASSPHRASE] +sys_cfg_max_sta_num [STA_NUM] +sys_cfg_retry_limit [RETRY_LIMIT] +sys_cfg_sticky_tim_config [ENABLE] [<DURATION> <STICKY_BIT_MASK>] +sys_cfg_sticky_tim_sta_mac_addr [CONTROL] [STA_MAC_ADDRESS] +sys_cfg_2040_coex [ENABLE] +sys_cfg_eapol_pwk_hsk [<TIMEOUT> <RETRIES>] +sys_cfg_eapol_gwk_hsk [<TIMEOUT> <RETRIES>] +sta_filter_table <FILTERMODE> <MACADDRESS_LIST> +regrdwr <TYPE> <OFFSET> [value] +memaccess <ADDR> [value] +rdeeprom <offset> <byteCount> +cfg_data <type> [*.conf] +sys_cfg_80211d [state STATE] [country COUNTRY] +uap_stats +sys_cfg_tdls_ext_cap [CONFIG_FILE] +sys_cfg_restrict_client_mode [<ENABLE> [MODE_CONFIG]] + +------------------- +Details of Commands +------------------- + +version +------- + "./uaputl.exe -v" + + This command prints the uAP utility version information. + +debug_level +----------- + "./uaputl.exe -d <debug_level>" + + The supported debug_level are: + 0 - no debug + 1 - enable MSG_DEBUG + 2 - enable all the debug + This command use to control the debug level of uaputl.exe. + + Example: + ./uaputl.exe -d 2 sys_config + Enable all the debug in uaputl.exe + +sys_config +---------- + "./uaputl.exe sys_config [CONFIG_FILE]" + This command is used to set or get the current settings of the Micro AP. + + The supported options are: + CONFIG_FILE is file contain all the Micro AP settings. + empty - Get current Micro AP settings + + Example: + ./uaputl.exe sys_config + Get current settings of the Micro AP. + + ./uaputl.exe sys_config config/uaputl.conf + Load Micro AP's settings from uaputl.conf file and set. + +bss_config +---------- + "./uaputl.exe bss_config [CONFIG_FILE]" + This command is used to set or get the current settings of the BSS. + + The supported options are: + CONFIG_FILE is file contain all the BSS settings. + empty - Get current BSS settings + + Example: + ./uaputl.exe bss_config + Get current settings of the BSS. + + ./uaputl.exe bss_config config/uaputl.conf + Load BSS settings from uaputl.conf file and set. + +sys_info +-------- + "./uaputl.exe sys_info" + + This command returns system information such as firmware version number + and HW information. + +sys_reset +--------- + "./uaputl.exe sys_reset" + + This command is used to reset the Micro AP back to its initial state. + For example, this can be used to recover from a serious error, or before + creating a new BSS. + + This command has the following effects: + 1. The WLAN hardware MAC is reset. + 2. All MIB variables are initialized to their respective default + values. + 3. The firmware internal variables are reset to their respective + default values. + 4. The firmware state machines are reset to their respective initial + states. + +bss_start +--------- + "./uaputl.exe bss_start" + + This command starts the BSS. + There is no error for redundant bss_start command. + +bss_stop +-------- + "./uaputl.exe bss_stop" + + This command stops the BSS. The command causes the firmware to: + 1. Deauthenticate all associated client stations. + 2. Turn off the radio (hence stopping beaconing). + There is no error for redundant bss_stop command. + +sta_list +-------- + "./uaputl.exe sta_list" + + This command returns the list of client stations that are currently + associated with the AP. + + The output is formatted as shown below, for each STA: + "STA <STA_NUM> information: + ========================== + MAC Address: <STA MAC address> + Power mfg status: active|power save + Rssi: <RSSI_VALUE>" + +sta_deauth +---------- + "./uaputl.exe sta_deauth <STA_MAC_ADDRESS>" + + This command is used to de-authentciate a client station for any reason. + +radioctrl +---------- + "./uaputl.exe radioctrl [0|1]" + + This command is used to set or get the radio settings. + The supported options are: + 1 - Turn radio on + 0 - Turn radio off + empty - Get current radio setting + +txratecfg +---------- + "./uaputl.exe txratecfg [l] [m] [n]" + + This command is used to set/get the transmit data rate. + + Where + [l] is <format> + <format> - This parameter specifies the data rate format used in this command + 0: LG + 1: HT + 2: VHT + 0xff: Auto + + [m] is <index> + <index> - This parameter specifies the rate or MCS index + If <format> is 0 (LG), + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 6 Mbps + 5 9 Mbps + 6 12 Mbps + 7 18 Mbps + 8 24 Mbps + 9 36 Mbps + 10 48 Mbps + 11 54 Mbps + If <format> is 1 (HT), + 0 MCS0 + 1 MCS1 + 2 MCS2 + 3 MCS3 + 4 MCS4 + 5 MCS5 + 6 MCS6 + 7 MCS7 + 8 MCS8 + 9 MCS9 + 10 MCS10 + 11 MCS11 + 12 MCS12 + 13 MCS13 + 14 MCS14 + 15 MCS15 + 32 MCS32 + If <format> is 2 (VHT), + 0 MCS0 + 1 MCS1 + 2 MCS2 + 3 MCS3 + 4 MCS4 + 5 MCS5 + 6 MCS6 + 7 MCS7 + 8 MCS8 + 9 MCS9 + [n] is <nss> + <nss> - This parameter specifies the NSS. It is valid only for VHT + If <format> is 2 (VHT), + 1 NSS1 + 2 NSS2 + + Examples: + ./uaputl.exe txratecfg 0 3 : Set fixed Tx rate to 11 Mbps + ./uaputl.exe txratecfg 0 11 : Set fixed Tx rate to 54 Mbps + ./uaputl.exe txratecfg 1 3 : Set fixed Tx rate to MCS3 + ./uaputl.exe txratecfg 2 3 2 : Set fixed Tx rate to MCS3 for NSS2 + ./uaputl.exe txratecfg 0xff : Disable fixed rate and uses auto rate + ./uaputl.exe txratecfg : Read the current data rate setting + +antcfg +---------- + "./uaputl.exe antcfg [m] [n]" + + This command is used to set/get the transmit and receive antenna. + where value of m is: + Bit 0 -- Tx Path A + Bit 1 -- Tx Path B + Bit 0-1 -- Tx Path A+B + + where value of n is: + Bit 0 -- Rx Path A + Bit 1 -- Rx Path B + Bit 0-1 -- Rx Path A+B + The Tx path setting (m) is used if Rx path (n) is not provided. + + Examples: + ./uaputl.exe antcfg : Get Tx and Rx path + ./uaputl.exe antcfg 3 : Set Tx and Rx path to A+B + ./uaputl.exe antcfg 2 3 : Set Tx path to B and Rx path to A+B + +sys_cfg_ap_mac_address +---------------------- + "./uaputl.exe sys_cfg_ap_mac_address [AP_MAC_ADDRESS]" + + This command is used to set or get the AP MAC address. + + If no arguments are given, this command returns the current AP MAC + address. + Otherwise, this MAC address becomes the BSSID of the infrastructure + network created by the AP. + + Example: + ./uaputl.exe sys_cfg_ap_mac_address 00:50:43:20:aa:bb + Set AP MAC address to 00:50:43:20:aa:bb + + ./uaputl.exe sys_cfg_ap_mac_address + Get AP MAC address" + +sys_cfg_ssid +------------ + "./uaputl.exe sys_cfg_ssid [SSID]" + + This command is used to set or get the AP SSID. + + If no arguments are given, this command returns the current AP SSID. + While setting, the maximum length of the SSID can be 32 characters. + + Example: + ./uaputl.exe sys_cfg_ssid microap + Set AP ssid to "microap" + + ./uaputl.exe sys_cfg_ssid + Get AP ssid + +sys_cfg_beacon_period +--------------------- + "./uaputl.exe sys_cfg_beacon_period [BEACON_PERIOD]" + + This command is used to set or get the AP beacon period. + + If no arguments are given, this command returns the current AP beacon + period. + + Beacon period is represented in milliseconds. + + Example: + ./uaputl.exe sys_cfg_beacon_period 100 + Set AP beacon period to 100 TU + + ./uaputl.exe sys_cfg_beacon_period + Get AP beacon period + +sys_cfg_dtim_period +------------------- + "./uaputl.exe sys_cfg_dtim_period [DTIM_PERIOD] + + This command is used to set or get the AP DTIM period. + + If no arguments are given, this command returns the current AP DTIM + period. + + Example: + ./uaputl.exe sys_cfg_dtim_period 3 + Set AP DTIM period to 3 + + ./uaputl.exe sys_cfg_dtim_period + Get AP DTIM period + +sys_cfg_scan_channels +--------------------- + "./uaputl.exe sys_cfg_scan_channels [CHANNEL[.BAND]]" + + This command is used to set or get the AP's scan channel list. + + If no arguments are given, this command returns the scan channel list. + If BAND is 0, channel is set in 2.4 GHz band and if BAND is 1, channel is set to 5GHz. + Channels from only one of the bands should be specified. + Each CHANNEL.BAND pair must be separated by a space. BAND parameter is optional. + + Example: + ./uaputl.exe sys_cfg_scan_channels 1 11 6 + Set AP scan channel list to 1 11 6 + + ./uaputl.exe sys_cfg_scan_channels 11.0 6.0 + Set AP scan channel list to 11 6 + + ./uaputl.exe sys_cfg_scan_channels + Get AP scan channel list + + ./uaputl.exe sys_cfg_scan_channels 8.1 16.1 34 + Set AP scan channel list to 8 16 and 34 in 5GHz band. + +sys_cfg_channel +--------------- + "./uaputl.exe sys_cfg_channel [CHANNEL] [MODE]" + + This command is used to set or get the AP radio channel. + + If no arguments are given, this command returns the current AP radio + channel. + + MODE: band config mode. + Bit 0: automatic channel selection (ACS) enable/disable + Bit 1: secondary channel is above primary channel enable/disable(only allow for channel 1-7) + Bit 2: secondary channel is below primary channel enable/disable(only allow for channel 5-11) + For 'a' band channel: + Bit 1: secondary channel is above primary channel enable/disable + Bit 2: secondary channel is below primary channel enable/disable + Only following pairs of channels are valid for secondary channel setting in 5GHz band. + 36, 40 + 44, 48 + 52, 56 + 60, 64 + 100, 104 + 108, 112 + 116, 120 + 124, 128 + 132, 136 + 149, 153 + 157, 161 + + Example: + ./uaputl.exe sys_cfg_channel 6 + Set AP radio channel to 6, and no secondary channel. + + ./uaputl.exe sys_cfg_channel 11 0 + Set AP radio channel to 11 with Manual Channel Select. + + ./uaputl.exe sys_cfg_channel 0 1 + Set AP to ACS. + + ./uaputl.exe sys_cfg_channel + Get AP radio channel + + ./uaputl.exe sys_cfg_channel 6 2 + Set AP primary radio channel to 6, and secondary channel is above. + ./uaputl.exe sys_cfg_channel 6 4 + Set AP primary radio channel to 6, and secondary channel is below + ./uaputl.exe sys_cfg_channel 0 3 + Set AP to ACS mode, and secondary channel is above. + ./uaputl.exe sys_cfg_channel 0 5 + Set AP to ACS mode, and secondary channel is below. + ./uaputl.exe sys_cfg_channel 36 2 + Set AP primary radio channel to 36, and secondary channel is above. + ./uaputl.exe sys_cfg_channel 40 4 + Set AP primary radio channel to 40, and secondary channel is below. + +sys_cfg_channel_ext +--------------- + "./uaputl.exe sys_cfg_channel_ext [CHANNEL] [BAND] [MODE]" + + This command is used to set or get the AP radio channel. + + If no arguments are given, this command returns the current AP radio + channel. + + BAND: 0 : 2.4GHz operation + 1 : 5GHz operation + MODE: band config mode. + Bit 0: automatic channel selection (ACS) enable/disable + Bit 1: secondary channel is above primary channel enable/disable(only allow for channel 1-7) + Bit 2: secondary channel is below primary channel enable/disable(only allow for channel 5-11) + For 'a' band channel: + Bit 1: secondary channel is above primary channel enable/disable + Bit 2: secondary channel is below primary channel enable/disable + Only following pairs of channels are valid for secondary channel setting in 5GHz band. + 36, 40 + 44, 48 + 52, 56 + 60, 64 + 100, 104 + 108, 112 + 116, 120 + 124, 128 + 132, 136 + 149, 153 + 157, 161 + + Example: + ./uaputl.exe sys_cfg_channel_ext 6 +Set AP radio channel to 6, and no secondary channel. + + ./uaputl.exe sys_cfg_channel_ext 11 0 0 + Set AP radio channel to 11 in 2.4GHz band with Manual Channel Select. + + ./uaputl.exe sys_cfg_channel_ext 0 0 1 + Set AP to ACS mode and 2.4GHz band. + + ./uaputl.exe sys_cfg_channel_ext 8 0 + Set AP to channel 8 and 2.4GHz band. + + ./uaputl.exe sys_cfg_channel_ext 8 1 + Set AP to channel 8 and 5GHz band. + + ./uaputl.exe sys_cfg_channel_ext 36 1 + Set AP to channel 36 and 5GHZ band. + + ./uaputl.exe sys_cfg_channel_ext + Get AP radio channel, band and mode. + + ./uaputl.exe sys_cfg_channel_ext 6 0 2 + Set AP primary radio channel to 6, band to 2.4GHz and secondary channel is above. + ./uaputl.exe sys_cfg_channel_ext 6 0 4 + Set AP primary radio channel to 6, band to 2.4GHz and secondary channel is below + ./uaputl.exe sys_cfg_channel_ext 0 0 3 + Set AP to ACS mode, band to 2.4GHz and secondary channel is above. + ./uaputl.exe sys_cfg_channel_ext 0 0 5 + Set AP to ACS mode, band to 2.4GHz and secondary channel is below. + ./uaputl.exe sys_cfg_channel_ext 36 1 2 + Set AP primary radio channel to 36, band to 5GHz and secondary channel is above. + ./uaputl.exe sys_cfg_channel_ext 40 1 4 + Set AP primary radio channel to 40, band to 5GHz and secondary channel is below. + +sys_cfg_rates +------------- + "./uaputl.exe sys_cfg_rates [RATES]" + + If 'Rate' provided, a 'set' is performed else a 'get' is performed + RATES is provided as a set of data rates, in unit of 500 kilobits + A rate with MSB bit is basic rate, i.e 0x82 is basic rate. + + 'set' will not allowed after bss start. + + Valid rates: 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 + Non-Basic rates: 0x02, 0x04, 0x0b, 0x16, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c + Basic rates: 0x82, 0x84, 0x8b, 0x96, 0x8C, 0x92, 0x98, 0xA4, 0xB0, 0xC8, 0xE0, 0xEc + + Each rate must be separated by a space. + + Example: + ./uaputl.exe sys_cfg_rates 0x82 0x84 0x96 0x0c 0x12 0x18 + ./uaputl.exe sys_cfg_rates + +sys_cfg_rates_ext +----------------- + "./uaputl.exe sys_cfg_rates_ext [rates RATES] [mbrate RATE]" + + If 'Rate' provided, a 'set' is performed else a 'get' is performed. + RATES is provided as a set of data rates, in unit of 500 kilobits + A rate with MSB bit is basic rate, i.e 0x82 is basic rate. + If only operational rates is provided, MCBC rate and unicast rate will be set to auto. + + Valid rates: 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 + Non-Basic rates: 0x02, 0x04, 0x0b, 0x16, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c + Basic rates: 0x82, 0x84, 0x8b, 0x96, 0x8C, 0x92, 0x98, 0xA4, 0xB0, 0xC8, 0xE0, 0xEc + Rates 2, 4, 11 and 22 (in units of 500 Kbps) must be present in either of + basic or non-basic rates. If OFDM rates are enabled then 12, 24 and 48 (in + units of 500 Kbps) must be present in either basic or non-basic rates. + + Each rate must be separated by a space. + + rates followed by RATES for setting operational rates. + mbrate followed by RATE for setting multicast and broadcast rate. + + operational rates only allow to set before bss start. + + Example: + ./uaputl.exe sys_cfg_rates_ext rates 0x82 0x04 11 0x96 12 24 48 mbrate 0x16 + Set AP operation rates to 0x82,0x04,11,0x96,12,24,48, multicast rate to 0x16 + ./uaputl.exe sys_cfg_rates_ext rates 0x82 0x04 11 0x96 12 24 48 + Set AP operation rates to 0x82,0x04,11,0x96,12,24,48. + +sys_cfg_tx_power +---------------- + "./uaputl.exe sys_cfg_tx_power [TX_POWER]" + + This command is used to set or get the AP Tx power. + + If no arguments are given, this command returns the current AP Tx power. + + Tx power level is represented in dBm. + + Example: + ./uaputl.exe sys_cfg_tx_power 13 + Set AP Tx power to 13 dBm + + ./uaputl.exe sys_cfg_tx_power + Get AP Tx power + +sys_cfg_bcast_ssid_ctl +---------------------- + "./uaputl.exe sys_cfg_bcast_ssid_ctl [0|1|2]" + + This command is used to set or get the SSID broadcast feature setting. + + The supported options are: + 0 - Disable SSID broadcast, send empty SSID (length=0) in beacon. + 1 - Enable SSID broadcast + 2 - Disable SSID broadcast, clear SSID (ACSII 0) in beacon, but keep the original length + empty - Get current SSID broadcast setting + + When broadcast SSID is enabled, the AP responds to probe requests from + client stations that contain null SSID. + + When broadcast SSID is disabled (sys_cfg_bcast_ssid_ctl = 0/2), the AP: + 1. Does not respond to probe requests that contain null SSID. + 2. when sys_cfg_bcast_ssid_ctl = 0, generates beacons that contain null SSID (length=0). + 3. when sys_cfg_bcast_ssid_ctl = 2, clear SSID (ACSII 0) in beacon and keep the original length + + Example: + ./uaputl.exe sys_cfg_bcast_ssid_ctl 0 + Disable SSID broadcast, send empty SSID (length=0) in beacon. + + ./uaputl.exe sys_cfg_bcast_ssid_ctl 1 + Enable SSID broadcast + + ./uaputl.exe sys_cfg_bcast_ssid_ctl 2 + Disable SSID broadcast, clear SSID (ACSII 0) in beacon, but keep the original length + + ./uaputl.exe sys_cfg_bcast_ssid_ctl + Get SSID broadcast setting + +sys_cfg_preamble_ctl +-------------------- + "./uaputl.exe sys_cfg_preamble_ctl" + + This command is used to get type of preamble. + + Example: + ./uaputl.exe sys_cfg_preamble_ctl + Get AP preamble setting + +sys_cfg_bss_status +-------------------- + "./uaputl.exe sys_cfg_bss_status" + + This command is used to get current BSS status. + + Example: + ./uaputl.exe sys_cfg_bss_status + Get current BSS status + +sys_cfg_rts_threshold +--------------------- + "./uaputl.exe sys_cfg_rts_threshold [RTS_THRESHOLD]" + + This command is used to set or get the RTS threshold value. + + If no arguments are given, this command returns the current RTS threshold + value. + + Example: + ./uaputl.exe sys_cfg_rts_threshold 2347 + Set AP RTS threshold to 2347 + + ./uaputl.exe sys_cfg_rts_threshold + Get AP RTS threshold + +sys_cfg_frag_threshold +---------------------- + "./uaputl.exe sys_cfg_frag_threshold [FRAG_THRESHOLD]" + + This command is used to set or get the Fragmentation threshold value. + + If no arguments are given, this command returns the current Fragmentation threshold + value. + + Example: + ./uaputl.exe sys_cfg_frag_threshold 2346 + Set AP Fragmentation threshold to 2346 + + ./uaputl.exe sys_cfg_frag_threshold + Get AP Fragmentation threshold + + Note: Please use aggrpriotbl command to disable the AMPDU/AMSDU aggregation when frag_threshold is set. + +sys_cfg_rsn_replay_prot +----------------------- + "./uaputl.exe sys_cfg_rsn_replay_prot [1|0]" + + This command is used to enable or disable RSN replay protection. + + The supported options are: + 0 - Disable RSN replay protection + 1 - Enable RSN replay protection + empty - Get current RSN replay protection setting + + Example: + ./uaputl.exe sys_cfg_rsn_replay_prot 1 + Enable RSN replay protection + + ./uaputl.exe sys_cfg_rsn_replay_prot + Get RSN replay protection setting + +sys_cfg_tx_beacon_rate +-------------------- + "./uaputl.exe sys_cfg_tx_beacon_rate [TX_BEACON_RATE]" + + This command is used to set or get the Tx beacon rate settings. + + The supported options are: + 0 - Auto rate + >0 - Set specified beacon rate + empty - Get current beacon rate + + Tx beacon rate is represented in units of 500 kbps. While setting Tx beacon + rates, only zero or rates currently configured are allowed. + + Following is the list of supported rates in units of 500 Kbps: + 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 + 0x02, 0x04, 0x0b, 0x16, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c + + Example: + ./uaputl.exe sys_cfg_tx_beacon_rate 0x04 + Set AP Tx beacon rate to 2 M + + ./uaputl.exe sys_cfg_tx_beacon_rate 4 + Set AP Tx data beacon to 2 M + + ./uaputl.exe sys_cfg_tx_beacon_rate + Get AP Tx beacon rate + +sys_cfg_mcbc_data_rate +---------------------- + "./uaputl.exe sys_cfg_mcbc_data_rate [MCBC_DATA_RATE]" + + This command is used to set or get the MCBC data rate to use for multicast + or broadcast packet transmission. + + The supported options are: + 0 - Auto rate + >0 - Set specified MCBC data rate + empty - Get current MCBC data rate + + MCBC data rate is represented in units of 500 kbps. While setting MCBC data + rates, only zero or one of rates currently configured as basic rates are allowed. + + For example: If current basic rates is "0x82 0x84 0x8b 0x96", then the allowed + values for MCBC data rate will be "0x2 0x4 0xb 0x16". + + Example: + ./uaputl.exe sys_cfg_mcbc_data_rate 22 + Set AP MCBC data rate to 11 M + + ./uaputl.exe sys_cfg_mcbc_data_rate 0 + Set AP MCBC data rate to auto + + ./uaputl.exe sys_cfg_mcbc_data_rate + Get AP MCBC data rate + +sys_cfg_pkt_fwd_ctl +------------------- + "./uaputl.exe sys_cfg_pkt_fwd_ctl [PKT_FWD_CTRL]" + + This command is used to set or get the packet forwarding control + settings. + + where PKT_FWD_CTRL is: + bit 0 -- Packet forwarding handled by Host (0) or Firmware (1) + bit 1 -- Intra-BSS broadcast packets are allowed (0) or denied (1) + bit 2 -- Intra-BSS unicast packets are allowed (0) or denied (1) + bit 3 -- Inter-BSS unicast packets are allowed (0) or denied (1) + empty - Get current packet forwarding setting + + Example: + ./uaputl.exe sys_cfg_pkt_fwd_ctl 1 + Set AP packet forwarding control in firmware to allow all packets + + ./uaputl.exe sys_cfg_pkt_fwd_ctl 6 + Set AP packet forwarding control in Host, only allow Inter-BSS unicast packets forwarding. + + ./uaputl.exe sys_cfg_pkt_fwd_ctl 8 + Set AP packet forwarding control in Host, only allow Intra-BSS packets forwarding. + + ./uaputl.exe sys_cfg_pkt_fwd_ctl 0 + Set AP packet forwarding control in Host, allow Intra-BSS packets and + Inter-BSS unicast packets forwarding. + + ./uaputl.exe sys_cfg_pkt_fwd_ctl + Get AP packet forwarding control + +sys_cfg_sta_ageout_timer +------------------------ + "./uaputl.exe sys_cfg_sta_ageout_timer [STA_AGEOUT_TIMER]" + + This command is used to set or get the STA ageout value. + + Value of 0 will mean that stations will never be aged out. + + Minimum value for this is 100. Maximum allowed setting should be 864000. + + If no arguments are given, this command returns the current STA ageout + value. + + Ageout timer value is represented in units of 100 ms. + + Example: + ./uaputl.exe sys_cfg_sta_ageout_timer 1800 + Set AP STA ageout time to 180000 ms + + ./uaputl.exe sys_cfg_sta_ageout_timer + Get AP STA ageout time + +sys_cfg_ps_sta_ageout_timer +--------------------------- + "./uaputl.exe sys_cfg_ps_sta_ageout_timer [PS_STA_AGEOUT_TIMER]" + + This command is used to set or get the PS STA ageout value. + + Value of 0 will mean that stations will never be aged out. + + Minimum value for this is 100. Maximum allowed setting should be 864000. + + If no arguments are given, this command returns the current PS STA ageout + value. + + Ageout timer value is represented in units of 100 ms. + + Example: + ./uaputl.exe sys_cfg_ps_sta_ageout_timer 1800 + Set AP PS STA ageout time to 180000 ms + + ./uaputl.exe sys_cfg_ps_sta_ageout_timer + Get AP PS STA ageout time + +sys_cfg_auth +------------ + "./uaputl.exe sys_cfg_auth [AUTHMODE]" + + This command is used to set or get the AP authentication mode. + + The supported options are: + AUTHMODE : 0 - Open authentication + 1 - Shared key authentication + 255 - Auto (Open and Shared key) authentication + empty - Get current authentication mode + + Example: + ./uaputl.exe sys_cfg_auth 0 + Set AP authentication mode to Open. + + ./uaputl.exe sys_cfg_auth + Get AP authentication mode. + +sys_cfg_protocol +---------------- + "./uaputl.exe sys_cfg_protocol [PROTOCOL] [AKM_SUITE]" + + This command is used to set or get the encryption protocol. + + The supported options are: + PROTOCOL: + 1 No RSN + 2 WEP Static + 8 WPA + 32 WPA2 + 40 WPA, WPA2 Mixed Mode + empty - Get current encryption protocol + + AKM_SUITE: + bit 0 KEY_MGMT_EAP + bit 1 KEY_MGMT_PSK + bit 2 KEY_MGMT_NONE + bit 8 KEY_MGMT_PSK_SHA256 + + Example: + ./uaputl.exe sys_cfg_protocol 2 + Set AP encryption protocol to static WEP. + + ./uaputl.exe sys_cfg_protocol + Get AP encryption protocol. + + ./uaputl.exe sys_cfg_protocol 32 0x102 + Set AP encryption protocol to WPA2 and AKM SUITE to PSK and PSK SHA256 + + ./uaputl.exe sys_cfg_protocol 40 0x100 + Set AP encryption protocol to WPA+WPA2 and AKM SUITE to PSK SHA256 + +sys_cfg_pmf +----------- + "./uaputl.exe sys_cfg_pmf [MFPC] [MFPR]" + + This command is used to set or get the PMF settings. + + The supported options are: + MFPC: 0 - Mgmt frame protection not capable + 1 - Mgmt frame protection capable + + MFPR: 0 - Mgmt frame protection not required + 1 - Mgmt frame protection required + x - don't care if MFPC = 0 + + Example: + ./uaputl.exe sys_cfg_pmf 1 1 + Set AP's PMF params to Mgmt frames protection capable and required. + + ./uaputl.exe sys_cfg_pmf + Get AP's PMF params settings. + +sys_cfg_wep_key +--------------- + "./uaputl.exe sys_cfg_wep_key [INDEX ISDEFAULT Key_0] + [INDEX ISDEFAULT Key_1] + [INDEX ISDEFAULT Key_2] + [INDEX ISDEFAULT Key_3] + [INDEX]" + + This command is used to set or get the WEP key settings. + + The supported options are: + INDEX: 0 - KeyIndex is 0 + 1 - KeyIndex is 1 + 2 - KeyIndex is 2 + 3 - KeyIndex is 3 + ISDEFAULT: 0: KeyIndex is not the default + 1: KeyIndex is the default transmit key + + KEY_* : Key value. + empty - Get current WEP key settings for all the keys + INDEX - Only INDEX will get the key setting for the particular + KeyIndex. + + Example: + ./uaputl.exe sys_cfg_wep_key 0 1 55555 + Set AP's default transmit key to "55555", key index is 0. + + ./uaputl.exe sys_cfg_wep_key 0 1 12345678901234567890123456 + Set AP's default transmit key to "12345678901234567890123456", key index is 0. + + ./uaputl.exe sys_cfg_wep_key + Get AP all the WEP keys settings. + + ./uaputl.exe sys_cfg_wep_key 1 + Get WEP key setting for the KeyIndex = 1. + +sys_cfg_cipher +-------------- + "./uaputl.exe sys_cfg_cipher [PAIRWISE_CIPHER GROUP_CIPHER]" + + This command is used to set or get the key types for the pairwise and group key. + + The supported options are: + PAIRWISE_CIPHER: + 0 None + 4 TKIP + 8 AES CCMP + 12 AES CCMP + TKIP + GROUP_CIPHER: + 0 None + 4 TKIP + 8 AES CCMP + empty - Get current key types + + Valid combinations of [PAIRWISE_CIPHER GROUP_CIPHER] are: + [0 0], [4 4], [8 8], [12 4]. + + Example: + ./uaputl.exe sys_cfg_cipher 4 4 + Set AP's pairwise and group key's type to TKIP. + + ./uaputl.exe sys_cfg_cipher + Get AP's key types for the pairwise and group key. + +sys_cfg_pwk_cipher +------------------ + "./uaputl.exe sys_cfg_pwk_cipher [<PROTOCOL>] [PAIRWISE_CIPHER]" + + This command is used to set or get protocol and corresponding pairwise cipher settings. + + The supported options are: + PROTOCOL: + 0 None + 8 WPA + 32 WPA2 + PAIRWISE_CIPHER: + 0 None + 4 TKIP + 8 AES CCMP + 12 AES CCMP + TKIP + WPA/TKIP cipher cannot be used when uAP operates in 802.11n mode. + If only PROTOCOL is provided, pairwise cipher for that protocol is displayed. + empty - Get protocol and corresponding pairwise cipher settings. + + Example: + ./uaputl.exe sys_cfg_pwk_cipher 8 4 + Set AP's pairwise cipher to TKIP for WPA protocol. + + ./uaputl.exe sys_cfg_pwk_cipher 32 + Get AP's pairwise cipher for WPA2 protocol. + + ./uaputl.exe sys_cfg_pwk_cipher + Get AP's protocol and corresponding pairwise cipher settings. + +sys_cfg_gwk_cipher +------------------ + "./uaputl.exe sys_cfg_gwk_cipher [GROUP_CIPHER]" + + This command is used to set or get group cipher. + + The supported options are: + GROUP_CIPHER: + 0 None + 4 TKIP + 8 AES CCMP + empty - Get group cipher settings. + + Example: + ./uaputl.exe sys_cfg_gwk_cipher 8 + Set AP's group cipher to AES CCMP. + + ./uaputl.exe sys_cfg_gwk_cipher + Get AP's group cipher settings. + +sys_cfg_group_rekey_timer +------------------------- + "./uaputl.exe sys_cfg_group_rekey_timer [GROUP_REKEY_TIMER]" + + This command is used to set or get the AP group re-key time interval, in seconds. + + The supported options are: + GROUP_REKEY_TIMER is represented in seconds. This is only applicable + if the protocol is WPA or WPA2. Value of 0 will disable group re-key. + + empty - Get current group rekey timer + + Example: + ./uaputl.exe sys_cfg_group_rekey_timer 1800 + Set AP's group re-key time interval to 1800 s + + ./uaputl.exe sys_cfg_group_rekey_timer + Get AP's group re-key time interval. + +sys_cfg_wpa_passphrase +---------------------- + "./uaputl.exe sys_cfg_wpa_passphrase [PASSPHRASE]" + + This command is used to set or get the WPA or WPA2 passphrase. + + If no arguments are given, this command returns the current WPA or WPA2 + passphrase. + While setting, the maximum length of the passphrase can be 64 characters. + + Example: + ./uaputl.exe sys_cfg_wpa_passphrase 1234567890 + Set AP's WPA or WPA2 passphrase to "1234567890" + + ./uaputl.exe sys_cfg_wpa_passphrase + Get AP's WPA or WPA2 passphrase. + +sys_cfg_max_sta_num +------------------- + "./uaputl.exe sys_cfg_max_sta_num [STA_NUM]" + + This command is used to set or get the maximum number of stations allowed to connect to uAP. + + If no arguments are given, this command returns the configured maximum number of stations + allowed to connect to uAP and maximum number of stations supported. + + Example: + ./uaputl.exe sys_cfg_max_sta_num 2 + Set AP's maximum station number to 2 + + ./uaputl.exe sys_cfg_max_sta_num + Get AP's maximum station number configured and maximum station number supported. + +sys_cfg_retry_limit +------------------- + "./uaputl.exe sys_cfg_retry_limit [RETRY_LIMIT]" + + This command is used to set or get the retry limit to use for packet transmissions. + + The maximum retry_limit allowed is 14. + + If no arguments are given, this command returns the current retry limit value. + + Example: + ./uaputl.exe sys_cfg_retry_limit 2 + Set AP's retry limit value to 2 + + ./uaputl.exe sys_cfg_retry_limit + Get AP's retry limit value + +sys_cfg_sticky_tim_config +------------------------- + "./uaputl.exe sys_cfg_sticky_tim_config [ENABLE] [<DURATION> <STICKY_BIT_MASK>]" + + This command is used to set or get sticky TIM configuration. + + ENABLE is used to enable or disable sticky TIM feature. + Following are the valid values of ENABLE + 0- disable SticktyTIM + 1- enable StickyTIM (Both DURATION in beacons and STICKY_BIT_MASK must be provided) + 2- enable StickyTIM (enable sticky TIM without changing previous values of + DURATION and STICKY_BIT_MASK) + + When bit 0 of STICKY_BIT_MASK is set, TIM bit is made sticky and when + cleared, normal TIM bit updates resume. + When bit 1 of STICKY_BIT_MASK is set, MoreData bit is made sticky and when + cleared, normal MoreData bit updates resume. + STICKY_BIT_MASK = 0 is NOT a valid configuration value. + + If no argument is given, this command returns current sticky TIM configuration. + + Example: + ./uaputl.exe sys_cfg_sticky_tim_config + Get sticky TIM configuration. + + ./uaputl.exe sys_cfg_sticky_tim_config 0 + Disable sticky TIM feature. + + ./uaputl.exe sys_cfg_sticky_tim_config 1 30 1 + Enable sticky TIM feature with DURATION of 30 beacons and + STICKY_BIT_MASK set to 1. + +sys_cfg_sticky_tim_sta_mac_addr +------------------------------- + "./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr [CONTROL] [STA_MAC_ADDRESS]" + + This command is used to set or get sticky TIM control parameter for associated station. + + CONTROL when set to 1, sticky TIM bit for that station is activated. + When set to 0, sticky TIM bit for that station is deactivated. + + If no argument is given, it returns sticky TIM configuration for all associated stations. + If only STA_MAC_ADDRESS is provided, it returns sticky TIM configartion for that station. + + Example: + ./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr + Get sticky TIM configuration for all associated stations. + + ./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr 00:50:43:20:11:22 + Get control parameter for station 00:50:43:20:11:22. + + ./uaputl.exe sys_cfg_sticky_tim_sta_mac_addr 1 00:50:43:20:11:22 + Set control parameter for station with MAC address 00:50:43:20:11:22 to 1. + + Note:TIM bit for an associated STA is made sticky only if both below mentioned + conditions are satisfied + 1.Enable = 1 or 2 in most recently received sys_cfg_sticky_tim_config command, and + 2.Control = 1 in most recently received sys_cfg_sticky_tim_sta_mac_addr + with this station MAC address. + +sys_cfg_2040_coex +------------------------------- + "./uaputl.exe sys_cfg_2040_coex [ENABLE]" + + This command is used to set or get 20/40 BSS coexistence configuration. + + ENABLE when set to 0, disables 20/40 coex. + When set to 1, enables 20/40 coex. + + If no argument is given, it returns 20/40 BSS coex configuration. + + Example: + ./uaputl.exe sys_cfg_2040_coex + Get 20/40 BSS coexistence configuration. + + ./uaputl.exe sys_cfg_2040_coex 0 + Disable 20/40 BSS coexistence. + + ./uaputl.exe sys_cfg_2040_coex 1 + Enable 20/40 BSS coexistence. + + Note:20/40 BSS coex configuration can be set only before starting BSS. + +sys_cfg_eapol_pwk_hsk +--------------------- + "./uaputl.exe sys_cfg_eapol_pwk_hsk [<TIMEOUT> <RETRIES>]" + + This command is used to set or get pairwise handshake update timeout and + number of retries. + + Both TIMEOUT and number of RETRIES should be provided for a 'set'. + + If no arguments are given, this command returns timeout value and number + of + retries for pairwise key. + + Example: + ./uaputl.exe sys_cfg_eapol_pwk_hsk 50 2 + Set AP's pairwise key timeout to 50ms and number of retries to 2. + + ./uaputl.exe sys_cfg_eapol_pwk_hsk + Get AP's pairwise key timeout and number of retries. + +sys_cfg_eapol_gwk_hsk +--------------------- + "./uaputl.exe sys_cfg_eapol_gwk_hsk [<TIMEOUT> <RETRIES>]" + + This command is used to set or get groupwise handshake update timeout and + number of retries. + + Both TIMEOUT and number of RETRIES should be provided for a 'set'. + + If no arguments are given, this command returns timeout value and number + of retries for groupwise key. + + Example: + ./uaputl.exe sys_cfg_eapol_gwk_hsk 50 2 + Set AP's groupwise key timeout to 50ms and number of retries to 2. + + ./uaputl.exe sys_cfg_eapol_gwk_hsk + Get AP's groupwise key timeout and number of retries. + +sta_filter_table +---------------- + "./uaputl.exe sta_filter_table <FILTERMODE> [<MACADDRESS_LIST>]" + + This command is used to get or set the client station MAC address + filter table. + + The supported options are: + FILTERMODE : 0 - Disable filter table + 1 - Allow mac address specified in the allowed list + 2 - Block MAC addresses specified in the banned list + MACADDRESS_LIST is the list of MAC addresses to be acted upon. Each + MAC address must be separated with a space. Maximum of 16 MAC addresses + are supported. + + empty - Get current client station MAC address filter table. + + Example: + ./uaputl.exe sta_filter_table 0 + Disable filter table + + ./uaputl.exe sta_filter_table 1 00:50:43:20:aa:bb + Set AP's filter mode to allow, only MAC address "00:50:43:ab:bb" will be allowed. + + ./uaputl.exe sta_filter_table + Get AP's filter table settings. + +regrdwr +------- + "./uaputl.exe regrdwr <TYPE> <OFFSET> [value]" + + These commands are used to read the MAC, BBP and RF registers from the card. + TYPE can take 3 values, 1 - read/write MAC register + 2 - read/write BBP register + 3 - read/write RF register + + OFFSET specifies the offset location that is to be read. + This parameter can be specified either in decimal or in hexadecimal (by preceding the number with a "0x"). + + value if specified, then that value will be written to that offset in the specified register. Value should be + specified in hexadecimal. + + Example: + ./uaputl.exe regrdwr 1 0xa123 + read MAC register 0xa123 + + ./uaputl.exe regrdwr 1 0xa123 0xaa + write 0xaa to MAC register 0xa123 + + ./uaputl.exe regrdwr 2 0x0123 + read BBP register 0x0123 + + ./uaputl.exe regrdwr 2 0x0123 0xaa + write 0xaa to BBP register 0x0123 + + ./uaputl.exe regrdwr 3 0x0123 + read RF register 0x0123 + + ./uaputl.exe regrdwr 3 0x0123 0xaa + write 0xaa to RF register 0x0123 + +memaccess +--------- + "./uaputl.exe memaccess <ADDR> [value]" + This commands is used to read/write to a memory address + + ADDR specifies the address of the location that is to be read/write + This parameter can be specified either in decimal or in hexadecimal (by preceding the number with a "0x"). + + value if specified, then that value will be written to that address in the specified register. + + Example: + ./uaputl.exe memaccess 0xc00153e4 + read contents of memory location 0xc00153e4 + + + ./uaputl.exe memaccess 0xc00153e4 0xaabbccdd + write value 0xaabbccdd to memory location 0xc00153e4 + +rdeeprom +-------- + "./uaputl.exe rdeeprom <offset> <bytecount>" + + This command is used to read bytes from offset location on + EEPROM + + offset: 0,4,8,..., multiple of 4 + bytecount: 4-20, multiple of 4 + + Example: + ./uaputl.exe rdeeprom 200 12 + read 12 bytes from offset 200 ON EEPROM + +cfg_data +-------- + "./uaputl.exe cfg_data <type> [*.conf]" + + This command is used to set/get the configuration data to/from the firmware. + + type: 2 -- cal data + + Example: + ./uaputl.exe cfg_data 2 cal_data.conf + read cal_data from cal_data.conf and download to firmware. + ./uaputl.exe cfg_data 2 + read cal_data from firmware + +sys_cfg_80211d +-------------- + "./uaputl.exe sys_cfg_80211d [state STATE] [country COUNTRY]" + This command is used to set/get 802.11D specific parameters. + If no parameters are provided, this command returns state, country and + domain information. + + Allowed values for state are 0 for disable and 1 for enable. + COUNTRY is a two letter string input (derived from ISO 3166 code; + http://www.iso.org/iso/country_codes/iso_3166_code_lists/english_country_names_and_code_elements.htm + ) + + Countries are mapped with specific domain in file "80211d_domain.conf". In + order to set customize band setting, user can modify 80211d_domain.conf + file. + + Example: + ./uaputl.exe sys_cfg_80211d state 0 + To-disable + + ./uaputl.exe sys_cfg_80211d state 1 + To-enable + + ./uaputl.exe sys_cfg_80211d country IN + for using country as INDIA + + ./uaputl.exe sys_cfg_80211d state 1 country US + for enabling and setting country in single command. + +uap_stats +--------- + "./uaputl.exe uap_stats" + This command is used to get uAP statistics. + + Example: + ./uaputl.exe uap_stats + +pscfg +--------- + "./uaputl.exe pscfg [MODE] [CTRL INACTTO MIN_SLEEP MAX_SLEEP MIN_AWAKE MAX_AWAKE] + + This command is used to set or get the AP's power mode and power save params. + + The supported options are: + MODE : 0 - disable power mode + 2 - enable inactivity based power save mode + + PS PARAMS: + CTRL: 0 - disable protection frame Tx before PS + 1 - enable protection frame Tx before PS + INACTTO: Inactivity timeout in microseconds, default value is 200000 us + MIN_SLEEP: Minimum sleep duration in microseconds, default value 17000 us + MAX_SLEEP: Maximum sleep duration in microseconds, default value 17000 us + The value of MIN_SLEEP should be >= 5000 us. + The value of MAX_SLEEP should be <= beacon interval(when ctrl: 0). + The value of MAX_SLEEP should be <= 32000 us(when ctrl: 1). + + MIN_AWAKE: Minimum awake duration in microseconds, default value is 2000 us + MAX_AWAKE: Maximum awake duration in microseconds, default value is 2000 us + The value of MIN_AWAKE should be >= 2000 us. + MIN_AWAKE,MAX_AWAKE only valid when MODE is set to inactivity based power save mode. + + + empty - Get current power mode and power save params. + + Example: + ./uaputl.exe pscfg 0 + Disable AP's power mode. + + ./uaputl.exe pscfg 2 + Enable inactivity based power save mode. + + ./uaputl.exe pscfg 2 1 400000 20000 20000 10000 10000 + Enable inactivity based power save mode, enable protection, set inactivity timeout 400000 us + set minimum sleep duration to 20000 us, maximum sleep duration to 20000 us + and set minimum awake duration to 10000us, maximum awake duration to 10000 us + + ./uaputl.exe pscfg + Get current AP's power mode and power save params. + +hscfg +----- + ./uaputl.exe hscfg [condition [[GPIO# [gap]]]] + This command is used to configure the host sleep parameters. + + This command takes one (condition), two (condition and GPIO#) or three + (condition, GPIO# and gap) parameters for set. If no parameter provided, + get is performed. + + where Condition is: + bit 0 = 1 -- broadcast data + bit 1 = 1 -- unicast data + bit 2 = 1 -- mac event + bit 3 = 1 -- multicast data + bit 6 = 1 -- Wakeup when mgmt frame received. + + The host sleep mode will be cancelled if condition is set to 0xffff. + The default is 0x7. + + where GPIO is the pin number of GPIO used to wakeup the host. It could be any valid + GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO will be used instead). + The default is 0xff. + + where Gap is the gap in milliseconds between wakeup signal and wakeup event or 0xff + for special setting (host acknowledge required) when GPIO is used to wakeup host. + The default is 200. + + Examples: + ./uaputl.exe hscfg : Get current host sleep mode + ./uaputl.exe hscfg 0xffff : Cancel host sleep mode + ./uaputl.exe hscfg 3 : Broadcast and unicast data + Use GPIO and gap set previously + ./uaputl.exe hscfg 2 3 : Unicast data + Use GPIO 3 and gap set previously + ./uaputl.exe hscfg 2 1 0xa0 : Unicast data + Use GPIO 1 and gap 160 ms + ./uaputl.exe hscfg 2 0xff : Unicast data + Use interface (e.g. SDIO) + Use gap set previously + ./uaputl.exe hscfg 4 3 0xff : MAC event + Use GPIO 3 + Special host sleep mode + ./uaputl.exe hscfg 1 0xff 0xff : Broadcast data + Use interface (e.g. SDIO) + Use gap 255ms + +hssetpara +--------- + ./uaputl.exe hssetpara condition [[GPIO# [gap]]] + This command is used to configure the host sleep parameters. + + Note: + 1) The usages of parameters are the same as "hscfg" command. + 2) The parameters will be saved in the driver and be used when host suspends. + +sta_deauth_ext +-------------- + "./uaputl.exe sta_deauth_ext <STA_MAC_ADDRESS><REASON_CODE>" + + This command is used to de-authenticate a client station with specific reason code. + + Example: + ./uaputl.exe sta_deauth_ext 00:50:43:20:34:58 4 + deauth station 00:50:43:20:34:58 with IEEE reason code 4 (Disassociated due to inactivity) + +mic_err +------- + "./uaputl.exe mic_err <STA_MAC_ADDRESS>" + + This command is used to report station mic error. + + Example: + ./uaputl.exe mic_err 00:50:43:20:34:58 + report mic err for station 00:50:43:20:34:58 + +key_material +------------ + "./uaputl.exe key_material <MAC_ADDRESS> <KEY> [KEY_ID]" + + This command is used to set key. Get operation is not supported. + + MAC_ADDRESS: station mac address while setting station unicast key + ff:ff:ff:ff:ff:ff while setting multicast key + + KEY: hex string, allowed length should be 32 or 64. + KEY_ID: default value is 0. + + Example: + ./uaputl.exe key_material ff:ff:ff:ff:ff:ff 12345678901234567890123456789012 1 + set group key, key value is "12345678901234567890123456789012", key id is 1 + + ./uaputl.exe key_material 00:50:43:20:34:58 1234567890123456789012345678901234567890123456789012345678901234 + set station 00:50:43:20:34:58 unicast key, + key value is "1234567890123456789012345678901234567890123456789012345678901234" + +sys_cfg_custom_ie +----------------- + "./uaputl.exe sys_cfg_custom_ie [INDEX] [MASK] [IEBuffer]" + + This command is used to set or get custom IEs for management frames. + + The supported options are: + INDEX: 0 - IE Index is 0 + 1 - IE Index is 1 + 2 - IE Index is 2 + MAX IE Index depends on device memory. + + -1 - Append/Delete IE automatically + Delete will delete the IE from the matching IE buffer + Append will append the IE to the buffer with the same mask + MASK : Management subtype mask value as per bit definitions + : Bit 0 - Association request. + : Bit 1 - Association response. + : Bit 2 - Reassociation request. + : Bit 3 - Reassociation response. + : Bit 4 - Probe request. + : Bit 5 - Probe response. + : Bit 8 - Beacon. + MASK : MASK = 0 to clear the mask and the IE buffer + + IEBuffer: IE buffer to set in hexadecimal bytes. + The Buffer should not be space separated. + ( Maximum length = 256 bytes ) + empty - Get IE buffer, subtype mask settings for all the indices [0-3]. + INDEX - Only INDEX will get the IE buffer configured for the particular + Index. + + Example: + ./uaputl.exe sys_cfg_custom_ie + Get IE buffer, subtype mask settings for all indices. + + ./uaputl.exe sys_cfg_custom_ie 1 + Get IE buffer and subtype mask WEP key setting for the Index = 1. + + ./uaputl.exe sys_cfg_custom_ie 2 0 + Clear IE buffer and mask value for Index = 2. + + ./uaputl.exe sys_cfg_custom_ie 3 0x101 0xdd051234567890 + Set IE buffer and mask value for Index = 3. + + ./uaputl.exe sys_cfg_custom_ie -1 0x101 0xdd051234567890 + Append the specified IEBuffer at index with mask value of 0x101 + + ./uaputl.exe sys_cfg_custom_ie -1 0 0xdd051234567890 + Delete the specified IEBuffer from all the IEs. + + ./uaputl.exe sys_cfg_custom_ie 2 0 0xdd051234567890 + Delete the specified IEBuffer from the IEs at index 2. + +coex_config +----------- + "./uaputl.exe coex_config [CONFIG_FILE]" + This command is used to set or get the BT coex configuration settings. + + The supported options are: + CONFIG_FILE is file contain all the BT coex settings. + empty - Get current BT coex settings + + Example: + ./uaputl.exe coex_config + Get current BT coex settings. + + ./uaputl.exe coex_config uapcoex.conf + Load BT coex configuration settings from uapcoex.conf file and set. + +sys_cfg_wmm +----------- + "./uaputl.exe sys_cfg_wmm [qosinfo=<qosinfo>] + [0] + [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]" + + This command can be used set/get beacon WMM parameters + + The supported option are: + qosinfo: qos information. User can set only MSB. Valid values are 0x80 and 0x00. + Lower 4 bits are managed by FW. Hence, value read for qosinfo may have + lower 4 bits non-zero. + + AC_BE: 0 + AC_BK: 1 + AC_VI: 2 + AC_V0: 3 + AIFSN: AIFSN value + ECW_MAX: ECW max + ECW_MIN: ECW min + TX_OP: TXOP Limit + empty - Get current WMM parameters + When all the parameter are 0, wmm will be disabled. + + Example: + ./uaputl.exe sys_cfg_wmm 0 3 10 4 0 + Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + + ./uaputl.exe sys_cfg_wmm 1 7 10 4 0 + Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + + ./uaputl.exe sys_cfg_wmm 2 2 4 3 94 + Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94 + + ./uaputl.exe sys_cfg_wmm 3 2 3 2 47 + Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47 + + ./uaputl.exe sys_cfg_wmm + Get current wmm parameters + + ./uaputl.exe sys_cfg_wmm 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47 + Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94 + Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47 + + ./uaputl.exe sys_cfg_wmm qosinfo=0x80 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47 + Enable wmm PS mode. + Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94 + Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47 + + ./uaputl.exe sys_cfg_wmm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ./uaputl.exe sys_cfg_wmm 0 + Disable wmm + +sys_cfg_ap_wmm +----------- + "./uaputl.exe sys_cfg_ap wmm [0] + [AC_BE AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_BK AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VI AIFSN ECW_MAX ECW_MIN TX_OP] + [AC_VO AIFSN ECW_MAX ECW_MIN TX_OP]" + + This command can be used set/get AP WMM parameters + + The supported option are: + AC_BE: 0 + AC_BK: 1 + AC_VI: 2 + AC_V0: 3 + AIFSN: AIFSN value + ECW_MAX: ECW max + ECW_MIN: ECW min + TX_OP: TXOP Limit + empty - Get current AP WMM parameters + When all the parameter are 0, AP wmm will be disabled. + + Example: + ./uaputl.exe sys_cfg_ap_wmm 0 3 10 4 0 + Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + + ./uaputl.exe sys_cfg_ap_wmm 1 7 10 4 0 + Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + + ./uaputl.exe sys_cfg_ap_wmm 2 2 4 3 94 + Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94 + + ./uaputl.exe sys_cfg_ap_wmm 3 2 3 2 47 + Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47 + + ./uaputl.exe sys_cfg_ap_wmm + Get current AP wmm parameters + + ./uaputl.exe sys_cfg_ap_wmm 0 3 10 4 0 1 7 10 4 0 2 2 4 3 94 3 2 3 2 47 + Set AC_BE with AIFSN 3, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_BK with AIFSN 7, ECW_MAX 10, ECW_MIN 4 and TXOP 0 + Set AC_VI with AIFSN 2, ECW_MAX 4, ECW_MIN 3 and TXOP 94 + Set AC_VO with AIFSN 2, ECW_MAX 3, ECW_MIN 2 and TXOP 47 + + ./uaputl.exe sys_cfg_ap_wmm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ./uaputl.exe sys_cfg_ap_wmm 0 + Disable AP wmm + +addbapara +--------- + "./uaputl.exe addbapara [timeout txwinsize rxwinsize txamsdu rxamsdu]" + This command can be used to update the default ADDBA parameters. + + The supported options are: + timeout - This is the block ack timeout for ADDBA request. + 0 : Disable (recommended for throughput test) + 1 - 65535 : Block Ack Timeout in TU + txwinsize - Buffer size for ADDBA request. (32 is default value) + rxwinsize - Buffer size for ADDBA response. (16 is default value) + txamsdu - amsdu support for ADDBA request. (1 is default value) + 0 : Disable amsdu in ADDBA request. + 1 - Enable amsdu in ADDBA request. + rxamsdu - amsdu support for ADDBA response. (1 is default value) + 0 : Disable amsdu in ADDBA response. + 1 - Enable amsdu in ADDBA response. + empty - Get current ADDBA parameters. + + Current window size limit for Tx as well as Rx is 1023. + + Example: + ./uaputl.exe addbapara + Get the current addba params + ./uaputl.exe addbaparam 1000 64 8 0 0 + This will change the ADDBA timeout to (1000 * 1024) us, txwinsize to 64 and rxwinsize to 8 + and disable AMSDU in ADDBA request/response. + + In case the ADDBA timeout value is updated then a ADDBA is sent for all streams + to update the timeout value. + + In case txwinsize and/or rxwinsize is updated, the effect could only be seen on + next ADDBA request/response. The current streams will not be affected with this + change. + + In case of txamdsdu/rxamsdu is updated, the effect could only be seen on + next ADDBA request/response. The current streams will not be affected with this + change. AMSDU in AMPDU stream will be enabled when AP support this feature + and AMSDU is enabled in aggrpriotbl. + +sys_cfg_11n +----------- + "./uaputl.exe sys_cfg_11n [ENABLE] [HTCAP] [AMPDU] [TXBFCAP] [HT_MCS_CAP]" + This command can be used set/get 802.11n parameters. + + The supported option are: + ENABLE: 0 - disable 802.11n in uap + 1 - enable 802.11n in uap + Note: If 802.11n is disabled, 802.11ac MUST be disabled at the same time + HTCAP: HT Capabilities info (default value is 0x111c) + Bit 15-13: Reserved set to 0 + Bit 12: DSS/CCK mode in 40MHz enable/disable + Bit 11-10: Reserved set to 0 + Bit 9-8: Reserved set to 0x01 + Bit 7: STBC enable/disable + Bit 6: Short GI in 40 Mhz enable/disable + Bit 5: Short GI in 20 Mhz enable/disable + Bit 4: Green field enable/disable + Bit 3-2: Reserved set to 1 + Bit 1: 20/40 Mhz enable disable. + Bit 0: LDPC enable/disable + AMPDU: A-MPDU Parameter (default value is 0x03) + Bit 7-5: Reserved set to 0 + Bit 4-2: Minimum MPDU Start spacing + Set to 0 for no restriction + Set to 1 for 1/4 us + Set to 2 for 1/2 us + Set to 3 for 1 us + Set to 4 for 2 us + Set to 5 for 4 us + Set to 6 for 8 us + Set to 7 for 16 us + Bit 1-0: Max A-MPDU length + TXBFCAP: TX Beamforming capabilities + Bit 0 : Implicit TX BF receiving capable + Bit 1 : RX staggered sounding capable + Bit 2 : TX staggered sounding capable + Bit 3 : RX NDP capable + Bit 4 : TX NDP capable + Bit 5 : Implicit TX BF capable + Bit 6-7 : Calibration + 0: - not supported + 1: - STA can respond to a calibration request using + the CSI Report, but cannot initiate calibration + 2: - reserved + 3: - STA can both initiate and respond to a calibration request + Bit 8 : Explicit CSI TX BF capable + Bit 9 : Explicit non-compressed steering capable + Bit 10 : Explicit compressed steering capable + Bit 11-12: Explicit TX BF CSI feedback + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 13-14: Explicit non-compressed BF feedback capable + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 15-16: Explicit compressed BF feedback capable + 0: - not supported + 1: - delayed feedback + 2: - immediate feedback + 3: - delayed and immediate feedback + Bit 17-18: Minimal grouping + 0: - no grouping (STA supports groups of 1) + 1: - groups of 1, 2 + 2: - groups of 1, 4 + 3: - groups of 1, 2, 4 + Bit 19-20: CSI number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 21-22: Non-compressed steering number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 23-24: Compressed steering number of beamformer antennas supported + 0: - single TX antenna sounding + 1: - 2 TX antenna sounding + 2: - 3 TX antenna sounding + 3: - 4 TX antenna sounding + Bit 25-26: CSI max number of rows beamformer supported + 0: - single row of CSI + 1: - 2 rows of CSI + 2: - 3 rows of CSI + 3: - 4 rows of CSI + Bit 27-28: Channel estimation capability + 0: - 1 space time stream + 1: - 2 space time streams + 2: - 3 space time streams + 3: - 4 space time streams + Bit 29-31: Reserved + HT_MCS_MAP: MCS rate bitmap + Bit 0-7 : MCS_SET_0 + Bit 15-8 : MCS_SET_1 + empty - Get current 802.11n parameters. + + Example: + ./uaputl.exe sys_cfg_11n 1 0x117e 3 + enable 802.11n, and set HT Capabilities info to 0x117e, and A-MPDU Parameter to 0x03 + ./uaputl.exe sys_cfg_11n 1 0x117e 3 0xFFFFFFFF + enable 802.11n, and set HT Capabilities info to 0x117e, A-MPDU + Parameter to 0x03 and TX Beamforming capabilities to 0xFFFFFFFF + ./uaputl.exe sys_cfg_11n 1 0x117e 3 0xFFFFFFFF 0x00100023 + enable 802.11n, and set HT Capabilities info to 0x117e, A-MPDU + Parameter to 0x03, TX Beamforming capabilities to 0xFFFFFFFF and + MCS rate bitmap to 0x00100023. + ./uaputl.exe sys_cfg_11n 0 + disable 802.11n in uap + ./uaputl.exe sys_cfg_11n + Get current 802.11n parameters + +aggrpriotbl +----------- + "./uaputl.exe aggrpriotbl <m0> <n0> <m1> <n1> ... <m7> <n7>" + This command is used set/get the priority table for AMPDU/AMSDU traffic per tid. + This command can also be used to disable AMPDU/AMSDU for a given tid. + In case of AMPDU this priority table will be used to setup block ack (to make + sure the highest priority tid always uses AMPDU as we have limited AMPDU streams) + + The supported option are: + <m0> <n0> <m1> <n1> ... <m7> <n7> + + <mx> - This is priority for Tid0 for AMPDU packet. A priority could be any + values between 0 - 7, 0xff to disable aggregation. + <nx> - This is priority for Tid0 for AMSDU packet. A priority could be any + values between 0 - 7, 0xff to disable aggregation. + + empty - Get current priority table for AMPDU/AMSDU traffic. + + Example: + ./uaputl.exe aggrpriotbl + This command will get the current Priority table for AMPDU and AMSDU. + <2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255>. This is read as + <"Prio for AMPDU for Tid0" "Prio for AMSDU for Tid0" + "Prio for AMPDU for Tid1" "Prio for AMSDU for Tid1" and so on + + ./uaputl.exe aggrpriotbl 2 2 0 0 1 1 3 3 4 4 5 5 255 255 255 255 + This will set the priority table for AMPDU and AMSDU + Priority for Tid0/AMPDU = 2, Tid0/AMSDU = 2, Tid1/AMPDU = 0, Tid1/AMSDU = 0 + and so on. Aggregation for Tid6 and Tid7 are disabled. + Here higher the priority number, higher the priority (i.e. 7 + has higher priority than 6). Similarly for AMSDU. + + ./uaputl.exe aggrpriotbl 0xff 2 0xff 0 0xff 1 0xff 3 0xff 4 0xff 5 0xff 0xff 0xff 0xff + This will disable AMPDU for all the TIDs but will still keep AMSDU enabled to Tid0 to Tid5 + + The default setting is 2 255 0 255 1 255 3 255 4 255 5 255 255 255 255 255. + + A delBA should be seen in case a disable happens on a TID for which AMPDU stream + is currently setup. + + Note:- This command should only be issued in disconnected state. + +addbareject +----------- + "./uaputl.exe addbareject <m0> <m1> ... <m7>" + This command is used set/get the addbareject table for all the TIDs. + This command can also be used to enable rejection of ADDBA requests for a given tid. + + The supported option are: + <m0> <m1> ... <m7> + <mX> - This can be 0/1 for TidX. 1 enables rejection of ADDBA request for TidX and + 0 would accept any ADDBAs for TidX. + empty - Get current addbareject table for all the TIDs. + + Example: + ./uaputl.exe addbareject + This command will get the current table. + [0 0 0 0 0 0 1 1]. ADDBA would be accepted for all TIDs except for TID [6,7]. + This is the default state. + + ./uaputl.exe addbareject 0 0 1 1 0 0 0 0 + This command will accept ADDBA requests for Tid [0,1,4,5,6,7] and reject ADDBA requests for Tid [2,3] + + ./uaputl.exe addbareject 1 1 1 1 1 1 1 1 + This will enable rejection of ADDBA requests for all Tids. + + Note:- This command should only be issued in disconnected state. + +sys_cfg_tdls_ext_cap +-------------------- + + "./uaputl.exe sys_cfg_tdls_ext_cap [CONFIG_FILE]" + + This command is used to set/get TDLS extended capability settings. + + If CONFIG_FILE is provided, a SET is performed , else a GET is performed. + + Examples: + ./uaputl.exe sys_cfg_tdls_ext_cap config/tdls_ext_cap.conf + Set TDLS extended capability parameters in the config file. + ./uaputl.exe sys_cfg_tdls_ext_cap + Get TDLS extended capability parameters. + +httxbfcfg +--------- + "./uaputl.exe httxbfcfg <ACTION> [ACT_DATA]" + + This command is used to configure the TX beamforming options. + + The supported options are: + ACTION: 0 - Control global parameters for beamforming + 1 - Performs NDP Sounding for PEER + 2 - TX BF interval in milliseconds + 3 - Enable/Disable beamforming/sounding for the indicated peer. + 4 - TX BF SNR Threshold for peer + ACT_DATA: Specific data for the above actions + For ACTION 0 - Beamforming enable/disable, sounding enable/disable, + FB type, snr_threshold, sounding interval, Beamformig mode + For ACTION 1 - PEER MAC and status + For ACTION 2 - TX BF interval + For ACTION 3 - PEER MAC + For ACTION 4 - PEER MAC and SNR + empty - Get action specific settings + + Examples: + ./uaputl.exe httxbfcfg 0 : Get current global configuration parameter + ./uaputl.exe httxbfcfg 2 00:50:43:20:BF:64 : Get the TX BF periodicity for a given peer + ./uaputl.exe httxbfcfg 3 : Get the list of MAC addresses that have + beamforming and/or sounding enabled + ./uaputl.exe httxbfcfg 4 : Get the list of PEER MAC, SNR tuples + programmed into the firmware. + ./uaputl.exe httxbfcfg 0 0 0 3 10 500 5 : Disable beamforming, sounding, set FB type + to 3, snr threshold to 10, sounding interval + to 500 ms and beamforming mode to 5 + ./uaputl.exe httxbfcfg 1 00:50:43:20:BF:64 : Perform NDP Trigger sounding to peer + 00:50:43:20:BF:64 + ./uaputl.exe httxbfcfg 2 00:50:43:20:BF:64 500 : Set TX BF periodicity for peer 00:50:43:20:BF:64 + to 500 milliseconds + ./uaputl.exe httxbfcfg 3 00:50:43:20:BF:43 1 0 3 : Enable beamforming, disable sounding and set + FB type to 3 for peer 00:50:43:20:BF:43 + ./uaputl.exe httxbfcfg 4 00:50:43:20:BF:24 43 : Set TX BF SNR threshold to peer + + +httxcfg + This command is used to configure various 11n specific configuration + for transmit (such as Short GI, Channel BW and Green field support) + + where <m> is <txcfg> + This is a bitmap and should be used as following + Bit 15-10: Reserved set to 0 + Bit 9-8: Rx STBC set to 0x01 + BIT9 BIT8 Description + 0 0 No spatial streams + 0 1 One spatial streams supported + 1 0 Reserved + 1 1 Reserved + Bit 7: STBC enable/disable + Bit 6: Short GI in 40 Mhz enable/disable + Bit 5: Short GI in 20 Mhz enable/disable + Bit 4: Green field enable/disable + Bit 3-2: Reserved set to 1 + Bit 1: 20/40 Mhz enable disable. + Bit 0: LDPC enable/disable + + When Bit 1 is set then firmware could transmit in 20Mhz or 40Mhz based + on rate adaptation. When this bit is reset then firmware will only + transmit in 20Mhz. + + where <n> is <band> + <band> - This is the band info for <txcfg> settings. + 0: Settings for both 2.4G and 5G bands + 1: Settings for 2.4G band + 2: Settings for 5G band + + Example: + ./uaputl.exe -i uapX httxcfg + This will display HT Tx configuration for 2.4G and 5G band. + + ./uaputl.exe -i uapX httxcfg 0x62 + This will enable 20/40 and Short GI but will disable Green field for 2.4G and 5G band. + + ./uaputl.exe -i uapX httxcfg 0x30 1 + This will enable Short GI 20 Mhz and Green field for 2.4G band. + + The default value is 0x20 for 2.4G and 0x62 for 5G. + + Note:- If 20/40 MHz support is disabled in htcapinfo, device will not transmit + in 40 MHz even 20/40 MHz is enabled in httxcfg. + +htstreamcfg + This command is used to set/get HT stream configuration. + The setting only takes effect in next association. + + Usage: + ./uaputl.exe -i uapX htstreamcfg [n] + + where <n> + 0x11: HT stream 1x1 mode + 0x22: HT stream 2x2 mode + + Examples: + ./uaputl.exe -i uapX htstreamcfg : Get current setting + ./uaputl.exe -i uapX htstreamcfg 0x11 : Set HT stream 1x1 mode + ./uaputl.exe -i uapX htstreamcfg 0x22 : Set HT stream 2x2 mode + +deepsleep +--------- + "./uaputl.exe deepsleep [MODE] [IDLE_TIME]" + This command is used to set/get auto deep sleep mode. + + The supported option are: + [MODE]: Enable/disable auto deep sleep mode (1/0) + [IDLE_TIME]: Idle time in milliseconds after which firmware will put the device + in deep sleep mode. Default value is 100 ms. + empty - Get current deep sleep mode. + + Example: + ./uaputl.exe deepsleep : Display auto deep sleep mode + ./uaputl.exe deepsleep 1 : Enable auto deep sleep mode, idle time unchanged + ./uaputl.exe deepsleep 0 : Disable auto deep sleep mode + ./uaputl.exe deepsleep 1 500 : Enable auto deep sleep mode with idle time 500 ms + + Note: + Deepsleep must be disabled before changing idle time. + +sdcmd52rw + This command is used to read/write a controller register in + Secure Digital I/O Interfaces. + + Usage: + "./uaputl.exe sdcmd52rw <function number> <register address> [value]" + + For SDIO MMC driver, only function 0 and 1 access is allowed. And there + is a limitation for function 0 write, only vendor specific CCCR registers + (0xf0 -0xff) are permiited. + + Examples: + ./uaputl.exe sdcmd52rw 1 3 : Issue cmd52 to read function 1, register 3. + ./uaputl.exe sdcmd52rw 1 1 0x3f : Issue cmd52 to write function 1, register 1 + with value 0x3f. + +txpwrlimit_cfg_get +txpwrlimit_2g_cfg_set +txpwrlimit_5g_cfg_set +------------------ + This command is used to set/get the configuration data of Tx power limitation. + Note: The configuration set should be issued when no STA is connected. + + Examples: + ./uaputl.exe hostcmd config/txpwrlimit_cfg.conf txpwrlimit_cfg_get + ./uaputl.exe hostcmd config/txpwrlimit_cfg.conf txpwrlimit_2g_cfg_set + ./uaputl.exe hostcmd config/txpwrlimit_cfg.conf txpwrlimit_5g_cfg_set + +rxpktcoal_cfg +------------- + "./uaputl.exe rxpktcoal_cfg [PKT-THRESHOLD] [TIMEOUT]" + + This is used to get/set RX packet coalescing paramters + + The supported option are: + [PKT-THRESHOLD]: count after which packets would be sent to host. Valid values 1-7 + [TIMEOUT]: timeout in ms after which packets would be sent to host. Valid values 1-4 + Coalescing is disabled if both or either of packet_thershold and delay is zero + + RX packet coalescing parameters can be changed only when device is in + idle state i.e. all interfaces are disconnected. + + Example: + ./uaputl.exe rxpktcoal_cfg : Display RX packet coalescing settings + ./uaputl.exe rxpktcoal_cfg 5 1 : Enable RX packet coalescing: packet count 5, delay 1. + ./uaputl.exe rxpktcoal_cfg 0 0 : Disable RX packet coalescing. + +tx_data_pause +------------- + "./uaputl.exe tx_data_pause [ENABLE][TX_BUF_CNT]" + + This command is used to set/get tx data pause settings. + + The supported option are: + [ENABLE]: Enable/disable pause tx events from firmware to host. + [TX_BUF_CNT]: Max number of TX buffers allowed for all PS clients + empty - Get current tx data pause settings + + Example: + ./uaputl.exe tx_data_pause : Display tx data pause settings + ./uaputl.exe tx_data_pause 1 : Enable pause tx event, max tx buffer number unchanged. + ./uaputl.exe tx_data_pause 0 : Disable pasue tx event, max tx buffer number unchanged. + ./uaputl.exe tx_data_pause 1 15 : Enable pause tx event, with max tx buffer number 15. + +vhtcfg + This command is used to set and get various 11ac specific configuration + for transmission and reception. For the SET operation, all paramaters + may be applied. For the GET operation, only the first two parameters are applied. + The 6th argument "rx_mcs_set" can be used to disbale/enable 802.11ac. + + where <j> is <band> + <band> - This is the band setting for the vhtcfg + 0: Settings for both 2.4G and 5G bands (for SET operation, 11N BW only) + 1: Settings for 2.4G band (for 11N BW only) + 2: Settings for 5G band + + where <k> is <txrx> + <txrx> - This parameter specifies the configuration of VHT operation for TX or/and VHT capabilities + 3: configuration of VHT capabilities (uAP only) + Note: For the UAP, only 3 is supported for txrx. + + where [l] is <bwcfg> + <bwcfg> - This parameter specifies the bandwidth (BW) configuration + applied to the vhtcfg. + If <txrx> is 3 (For uAP), + 0: Tx BW follows the BW (20/40 MHz) from 11N CFG + 1: Tx BW follows the BW (80/160/80+80 MHz) from VHT Capabilities + defined in <vhtcap> below for 5G band. + + where [m] is <vhtcap> + <vhtcap> - This parameter specifies the VHT capabilities info if <txrx> is 2 (association) + or the VHT Tx operations if <txrx> is 1 (Tx operations). + The VHT Tx operation should be a subset of VHT capabilities for association. + It is a bitmap and should be used as follows: + + Bit 31-30: Reserved and set to 0 + Bit 29: TX antenna pattern consistency + 1: antenna pattern does not change + 0: antenna pattern might change + Bit 28: RX antenna pattern consistency + 1: antenna pattern does not change + 0: antenna pattern might change + Bit 27-26: VHT link adaptation capable + 0: no feedback of VHT MFB from the STA + 1: unsolicted feedback of VHT MFB from the STA + 2: both response and unsolicted feedback of VHT MFB + from the STA + 3: reserved and set to 0 + Bit 25-23: Maximum A-MPDU length exponent + Bit 22: +HTC-VHT capable (1: enable. 0 disable) + Bit 21: VHT TXOP PS + Bit 20: MU beamformee capable (1: enable. 0 disable) + Bit 19: MU beamformer capable (1: enable. 0 disable) + Bit 18-16: Number of sounding dimensions (set to maximum-1 + if Bit 11 is 1. Otherwise, reserved and set to 0) + Bit 15-13: Compressed steering number of beamformer + antennas supported (set to maximum-1 if Bit 12 is 1. + Otherwise, reserved and set to 0) + Bit 12: SU beamformee capable (1: enable. 0 disable) + Bit 11: SU beamformer capable (1: enable. 0 disable) + Bit 10-8: Rx STBC + 0: no support + 1: support of 1 spatial stream + 2: support of 1-2 streams + 3: support of 1-3 spatial streams + 4: support of 1-4 spatial streams + 5-7: reserved and set to 0 + Bit 7: TX STBC (1: enable. 0 disable) + Bit 6: Short GI for 160 and 80+80 MHz (1: enable. 0 disable) + Bit 5: Short GI for 80 MHz (1: enable. 0 disable) + Bit 4: Rx LDPC (1: enable. 0 disable) + Bit 3-2: Supported channel width set. + 0: no support of either 160 or 80+80 MHz. + 1: support of 160 MHz + 2: support of both 160 and 80+80 MHz. + 3: reserved and set to 0. + Bit 1-0: Maximum MPDU length + 0: 3895 octets. + 1: 7991 octets. + 2: 11454 octets. + 3: reserved and set to 0. + + where [n] is <tx_mcs_map>, + <tx_mcs_map> - This parameter specifies the TX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations). + It is a bitmap and should be used as following + Bit 15-0: MCS map, which is defined as folows: + Bit 15-14: Max MCS for 8 SS + Bit 13-12: Max MCS for 7 SS + Bit 11-10: Max MCS for 6 SS + Bit 9-8: Max MCS for 5 SS + Bit 7-6: Max MCS for 4 SS + Bit 5-4: Max MCS for 3 SS + Bit 3-2: Max MCS for 2 SS + Bit 1-0: Max MCS for 1 SS + + where [o] is <rx_mcs_map>. + <rx_mcs_map> - This parameter specifies the RX MCS map. It may not be used for the STA if <txrx> is 1 (Tx operations). + It is a bitmap with the same sructure as for <tx_mcs_map> + rx_mcs_map = 0xffff : FW will disable 802.11ac + rx_mcs_map = others : FW will enable 802.11ac + + Note: The user setting of vhtcap may be overwritten by the driver + if the setting of those fields is beyond the hardware capabilities. + + Examples: + ./uaputl.exe -i uapX vhtcfg 2 3 : Get current VHT configuration in 5GHz for the uAP. + ./uaputl.exe -i uapX vhtcfg 2 3 0 0x000001f0 0xfffa 0xfffa + : Set the current/maximum VHT configuration in 5GHz for the uAP. + Both Tx and Rx supports MCS 0-9 for both 1 and 2 spatial streams. + ./uaputl.exe -i uapX vhtcfg 2 3 0 0x000001b0 + : Set the VHT capability information in 5GHz for the uAP, and keep the Tx/Rx MCS Map same as before. + +dfstesting +---------- + "./uaputl.exe dfstesting [<user_cac_pd> <user_nop_pd> <no_chan_change> <fixed_chan_num>]" + + This command is used to set/get DFS testing settings. + + The supported option are: + <user_cac_pd>: user-configured Channel Availability Check in msec + 0 = disable, use default period (60000) + 1-65535 = enable with that period + <user_nop_pd>: user-configured Non-Occupancy Period in sec + 0 = disable, use default period (1800) + 1-65535 = enable with that period + <no_chan_change>: enable/disable no channel change on radar + 0 = disable, 1 = enable (overrides below) + <fixed_chan_num>: user-configured channel to change to on radar + 0 = disable, 1-255 = enable with that channel + (channel validity for region, etc. is not checked) + (only takes effect if no_chan_change = 0) + + Example: + ./uaputl.exe dfstesting : Get current dfstesting settings + ./uaputl.exe dfstesting 2000 0 0 0 : user_cac=2sec, others disabled/default + ./uaputl.exe dfstesting 0 0 1 0 : only no_chan_change enabled + ./uaputl.exe dfstesting 0 120 0 64 : user_nop=2min, force chan 64 on radar + +cscount +--------------- + + "./uaputl.exe cscount [<channel_switch_count>]" + + This command is used to configure the number of beacons AP sends with channel switch IE, before channel change due to + radar detection. If not configured, <channel_switch_count> is set to 5 by default. + + The supported options are: + <channel_switch_count>: user configured channel switch count + 5-20 = configure with that value + + Example: + ./uaputl.exe cscount : Get user configured channel switch count value + ./uaputl.exe cscount 10 : Set channel switch count to 10 + + +mgmtframectrl +------------- + "./uaputl.exe mgmtframectrl [MASK]" + + This command is used to set/get management frame control mask. + + where the parameter [MASK] is the bit mask of management frame reception. + Following are the bit definitions. + Bit 0 : Association Request + Bit 1 : Association Response + Bit 2 : Re-Association Request + Bit 3 : Re-Association Response + Bit 4 : Probe Request + Bit 5 : Probe Response + Bit 8 : Beacon Frames + + Example: + ./uaputl.exe mgmtframectrl 0x101 + Set management frame control mask to 0x101. + +restrict_client_mode +-------------------- + "./uaputl.exe restrict_client_mode [<ENABLE> [MODE_CONFIG]]" + + This command is used to set/get the mode in which the ex-Stations can connect to the uAP. + If no arguments are given, this command returns the current configuration. + By default this feature will be disabled. + [ENABLE]: + 1: Enable the feature. + 0: Disable the feature. + [MODE_CONFIG]: config mode. + Bit 0: B only Mode. + Bit 1: A only Mode. + Bit 2: G only Mode. + Bit 3: N only Mode. + Bit 4: AC only Mode. + + Example: + ./uaputl.exe restrict_client_mode 1 0x8. + Only N mode clients will be able to connect. Association will fail for other clients. + + ./uaputl.exe restrict_client_mode 0. + This feature is disabled. + + Note: + The set operation should be performed before bss is started. + User should make sure that the mode advertized by uAP in beacons + (i.e. combination of supported/extended rates IE and capability IEs) + is superset of the mode allowed for ex-STA associations using this TLV. + +uap_oper_ctrl +------------- + "./uaputl.exe uap_oper_ctrl [control] [chanopt] [bandcfg] [channel]" + + This command is used to set/get uAP operation control when in-STA disconnected with ext-AP. + If no arguments are given, this command returns the current configuration. + + The supported options are: + <control> : 0 default, do nothing + 2 uAP stops and restart automatically + <chanopt> Specify which channel should be used when uap restarts automatically + 1: uap restarts on default 2.4G/channel 6 + 2: uap restart on band/channel configured by driver previously + 3: uap restart on band/channel configured by parameter bandcfg/channel + <bandcfg> This parameter specifies the bandwidth when chanopt is 3 + 0: 20Mhz + 2: 40Mhz + 3: 80Mhz + <channel> This parameter specifies the channel will be used when chanopt is 3 + + Example: + ./uaputl.exe uap_oper_ctrl 2 1 + uap stops and restart automatically when in-STA disconnects with ext-AP, + and restart on default 2.4G/channel 6. + + ./uaputl.exe uap_oper_ctrl 2 2 + uap stops and restart automatically when in-STA disconnects with ext-AP, + and restart on band/channel configured by driver previously. + + ./uaputl.exe uap_oper_ctrl 2 3 2 36 + uap stops and restart automatically when in-STA disconnects with ext-AP, + and restart on channel 36, bandwidth 40. + + ./uaputl.exe uap_oper_ctrl 0 + when in-STA disconnects with ext-AP, uap will stay on current operation channel. + + ./uaputl.exe uap_oper_ctrl + Get current uap operation control setting. +=============================================================================== + U S E R M A N U A L F O R MLANEVENT + +NAME +mlanevent.exe + +This tool can be used to listen for and obtain events from the uAP driver +through the netlink layer. + +---------------- +Supported events +---------------- +STA_DEAUTH +STA_ASSOC +BSS_START +BSS_IDLE +BSS_ACTIVE + +----------------- +Details of events +----------------- + +STA_DEAUTH +---------- + For this event, the following information is shown: + + Deauthenticated STA MAC address. + + Reason for deauthentication. + +STA_ASSOC +--------- + For this event, the following information is shown: + + STA MAC address. + +BSS_START +--------- + For this event, the following information is shown: + + AP MAC address. + +BSS_IDLE +-------- + For this event, there is no associated information. + +BSS_ACTIVE +---------- + For this event, there is no associated information. + +=============================================================================== + U S E R M A N U A L F O R IWPRIV + +NAME + This manual describes the usage of private commands used in Marvell MLAN + Linux UAP Driver. + + To use parameters as hex format, a '0x' must precede it for the parameters to + be parsed properly. + +SYNOPSIS + iwpriv <uapX> <command> [sub-command] ... + + iwpriv uapX version + iwpriv uapX verext + iwpriv uapX start + iwpriv uapX stop + iwpriv uapX bssstart + iwpriv uapX bssstop + iwpriv uapX fwreload <path> + iwpriv uapX apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,[SEC,][KEY,] + [CHANNEL,][PREAMBLE,][MAX_SCB,][END]" + +DESCRIPTION + Those commands are used to send additional commands to the Marvell MLAN + card via the Linux device driver. + + The uapX parameter specifies the network device that is to be used to + perform this command on. It could be uap0, uap1 etc. + +version + This is used to get the current version of the driver and the firmware. + +verext + Retrieve and display an extended version string from the firmware + + Usage: + iwpriv uapX verext [#] + + where [#] is an optional argument to retrieve a specific version string, + omission of the argument retrieves the 0 indexed string. + +start + Start the uAP driver. + + Usage: + iwpriv uapX start + +stop + Stop the uAP driver. + + Usage: + iwpriv uapX stop + +bssstart + Start the AP mode, so that uAP will start transmitting beacon. + + Usage: + iwpriv uapX bssstart + +bssstop + Stop the AP mode and disconnect all stations. uAP will stop + transmitting beacon as well. + + Usage: + iwpriv uapX bssstop + +fwreload + Reload the firmware. Here string "FW_PATH=" in the path + argument is mandatory part. + + Usage: + iwpriv uapX fwreload <path> + +apcfg + This command is used to set the AP configurations. Here string + "ASCII_CMD=AP_CFG" is minimum requirement in the ASCII string. + Note: BSS will be stopped then restarted if BSS is already started + when the command is received. + + Usage: + iwpriv uapX apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,[SEC=sec,] + [KEY=key,][CHANNEL=channel,][MAX_SCB=max_scb,][END]" + + Where the parameters are: + SSID: Set SSID to be used in beacon and probe response + [SEC]: Security modes - open, wep128, wpa-psk or wpa2-psk + 11n will be auto disabled in wep128 and wpa-psk mode + [KEY]: Encrypted key for wep128, wpa-psk or wpa2-psk, minimum 8 chars + [CHANNEL]: Channel to be selected + [MAX_SCB]: Maximum STA number + [END]: Optional termination in ASCII string + + Examples: + iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP" + : Set AP SSID to "TEST_uAP" + iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,SEC=open" + : Set AP SSID to "TEST_uAP" and + security mode is disabled + iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,SEC=WPA2-PSK,KEY=ecbe5facdbfe234a" + : Set AP SSID to "TEST_uAP" and security mode + to WPA2-SPK and encrypted key ecbe5facdbfe234a + iwpriv uap0 apcfg "ASCII_CMD=AP_CFG,SSID=TEST_uAP,CHANNEL=8" + : Set AP SSID to "TEST_uAP" and + set the AP channel to 8 + +===============================================================================
diff --git a/wlan_sd8897/README_WIFIDIRECT b/wlan_sd8897/README_WIFIDIRECT new file mode 100644 index 0000000..b56602a --- /dev/null +++ b/wlan_sd8897/README_WIFIDIRECT
@@ -0,0 +1,448 @@ + +=============================================================================== + + S E T U P I N S T R U C T I O N S F O R *WifiDirect* + +Driver,FW release: + +1. Make sure, bg_scan.conf,uaputl.conf has SSID starting with "DIRECT-" +2. Download uAP configuration and BG scan configuration. +3. This version of wifidirectutl breaks the backward compatibility and will work +with following releases - + 8797 >= 14.xx.16.p15 + >= 14.xx.11.p138 + 8766 >= 14.xx.11.p138 + 8787 >= 14.xx.9.p89 + + However, In case one needs to run the utility in backward compatibility mode + he can do so by running following command before running the utility - + + # export WIFIDIR_USE_FIXED_IE_INDICES=1 + + To disable backward comaptibility mode run following command - + + # export WIFIDIR_USE_FIXED_IE_INDICES=0 + +WPSWIFIDIRECT release: + +1. Modify the wifidirect.conf file to specify the correct HW addresses of + devices. The DUT mac address and peer mac address needs to be correctly + entered. + +=============================================================================== + U S E R M A N U A L F O R WIFIDIRECTUTL + +NAME + wifidirectutl + +This tool can be used to configure WifiDirect parameters. + +------------------ +Supported Commands +------------------ +wifidirect_mode [MODE] +wifidirect_config [*.conf] +wifidirect_params_config [*.conf] +wifidirect_action_frame <*.conf> | [<PeerAddr> <Category> <OuiSubtype> <DialogToken>] +wifidirect_discovery_request <*.conf> +wifidirect_discovery_response <*.conf> + +wifidirect_cfg_discovery_period [<MinDiscPeriod> <MaxDiscPeriod>] +wifidirect_cfg_intent [IntentValue] +wifidirect_cfg_capability [<DeviceCapability> <GroupCapability>] +wifidirect_cfg_noa <enable|disable> <index> [<counttype> <duration> <interval>] +wifidirect_cfg_opp_ps <enable|disable> [<CTWindow>] +wifidirect_cfg_invitation_list [mac_addr] +wifidirect_cfg_listen_channel [ListenChannel] +wifidirect_cfg_op_channel [OperatingChannel] +wifidirect_cfg_persistent_group_record [index] [role] + [<groupbss> <deviceId> <ssid> <psk>] [peermac1] [peermac2] +wifidirect_cfg_persistent_group_invoke [index] | <cancel> +wifidirect_cfg_presence_req_params [<type> <duration> <interval>] +wifidirect_cfg_ext_listen_time [<duration> <interval>] +wifidirect_cfg_provisioning_params [<action> <configMethods> <devicePassword>] +wifidirect_cfg_wps_params [<action>] + +wifidirect_mode [mode] +---------- + "./wifidirectutl <iface> wifidirect_mode [mode]" + + This command is used to setup various modes for wifidirect device. + The mode 2 can be used only when wifidirect is started using mode 1. + The mode 3 should not be used for uAP. + + The supported options are: + mode : 0 - stop wifidirect mode + 1 - start wifidirect mode + 2 - start wifidirect group owner mode + 3 - start wifidirect client mode + 4 - start wifidirect find phase + 5 - stop wifidirect find phase + empty - Get current wifidirect mode + + Example: + ./wifidirectutl <iface> wifidirect_mode 0 + Stop wifidirect mode. + + ./wifidirectutl <iface> wifidirect_mode 1 + Start wifidirect mode. + + ./wifidirectutl <iface> wifidirect_mode + Get current WIFIDIRECT start/stop mode. + +wifidirect_config +---------- + "./wifidirectutl <iface> wifidirect_config [*.conf]" + + This command is used to set/get the wifidirect configuration. + + Example: + ./wifidirectutl <iface> wifidirect_config wifidirect.conf + Read configuration from wifidirect.conf and apply it. + ./wifidirectutl <iface> wifidirect_config + Read existing wifidirect configuration and display it. + +wifidirect_params_config +---------- + "./wifidirectutl <iface> wifidirect_params_config [*.conf]" + + This command is used to set/get the wifidirect parameters configuration. + + Example: + ./wifidirectutl <iface> wifidirect_params_config wifidirect.conf + Read parameter configuration from wifidirect.conf and apply it. + ./wifidirectutl <iface> wifidirect_params_config + Read existing wifidirect parameters's configuration and display it. + +wifidirect_action_frame +---------- + "./wifidirectutl <iface> wifidirect_action_frame <*.conf> | <PeerAddr> <Category> <OUISubtype> <DialogToken>" + + This command is used to send action frames as specified in config file or on command line. + + Example: + ./wifidirectutl <iface> wifidirect_action_frame wifidirect.conf + Read action_frame from wifidirect.conf and send to peer. + ./wifidirectutl <iface> wifidirect_action_frame <PeerAddr> <Category> <OUISubtype> <DialogToken> + Read action frame parameters from command line and send to peer. + +wifidirect_discovery_request +---------- + "./wifidirectutl <iface> wifidirect_discovery_request <*.conf>" + + This command is used to send wifidirect discovery request packet. + + Example: + ./wifidirectutl <iface> wifidirect_discovery_request wifidirect.conf + Read discovery packet from wifidirect.conf and send to peer. + +wifidirect_discovery_response +---------- + "./wifidirectutl <iface> wifidirect_discovery_response <*.conf>" + + This command is used to send wifidirect discovery response packet. + + Example: + ./wifidirectutl <iface> wifidirect_discovery_response wifidirect.conf + Read discovery packet from wifidirect.conf and send to peer. + +wifidirect_cfg_discovery_period +---------- + "./wifidirectutl <iface> wifidirect_cfg_discovery_period [<MinDiscPeriod> <MaxDiscPeriod>]" + + This command is used to set or get minimum and maximum discovery period. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_discovery_period 10 20 + Set minimum discovery interval to 10 and maximum discovery + interval to 20. + + ./wifidirectutl <iface> wifidirect_cfg_discovery_period + Get minimum and maximum discovery interval. + +wifidirect_cfg_intent +---------- + "./wifidirectutl <iface> wifidirect_cfg_intent [IntentValue]" + + This command is used to set or get intent value. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_intent 12 + Set intent value to 12. + + ./wifidirectutl <iface> wifidirect_cfg_intent + Get Group Owner Intent. + +wifidirect_cfg_capability +---------- + "./wifidirectutl <iface> wifidirect_cfg_capability [<DeviceCapability> <GroupCapability>]" + + This command is used to set or get device capability and group capability. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_capability 10 20 + Set Device capability to 10 and group capability to 20 + + ./wifidirectutl <iface> wifidirect_cfg_capability + Get Device capability and group capability. + +wifidirect_cfg_noa +---------- + "./wifidirectutl <iface> wifidirect_cfg_noa <enable|disable> <index> [<counttype> <duration> <interval>]" + + This command is used to set or get NoA parameters like count_type, duration and + interval in ms when NoA is enabled. Valid value of index is [0, 1]. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_noa enable 0 10 50 100 + Enable NoA and set count to 10, duration to 50ms and interval to 100 ms + for index 0. + + ./wifidirectutl <iface> wifidirect_cfg_noa disable 1 + Disable NoA at index 1. + + ./wifidirectutl <iface> wifidirect_cfg_noa + Get NoA settings. + +wifidirect_cfg_opp_ps +---------- + "./wifidirectutl <iface> wifidirect_cfg_opp_ps <enable|disable> [<CTWindow>]" + + This command is used to set or get Opportunistic power save and CT window. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_opp_ps enable 50 + Set OppPS and CTwindow value to 50. + + ./wifidirectutl <iface> wifidirect_cfg_opp_ps disable + Disable OppPS. + + ./wifidirectutl <iface> wifidirect_cfg_opp_ps + Get OppPS and CT window. + +wifidirect_cfg_invitation_list +---------- + "./wifidirectutl <iface> wifidirect_cfg_invitation_list [mac_addr]" + + This command is used to set or get invitation list of peers. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_invitation_list 00:50:43:20:23:34 + Set 00:50:43:20:23:34 in invitation list of peers. + + ./wifidirectutl <iface> wifidirect_cfg_invitation_list + Get Invitation list of peers. + +wifidirect_cfg_listen_channel +---------- + "./wifidirectutl <iface> wifidirect_cfg_listen_channel [ListenChannel]" + + This command is used to set or get Listen channel. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_listen_channel 11 + Set Listen channel to 11. + + ./wifidirectutl <iface> wifidirect_cfg_listen_channel + Get Listen channel. + +wifidirect_cfg_op_channel +---------- + "./wifidirectutl <iface> wifidirect_cfg_op_channel [OperatingChannel]" + + This command is used to set or get Operating channel. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_op_channel 11 + Set Operating channel to 11. + + ./wifidirectutl <iface> wifidirect_cfg_op_channel + Get Operating channel. + +wifidirect_cfg_persistent_group_record +---------- + "./wifidirectutl <iface> [index] [role] + [<groupbss> <deviceId> <ssid> <psk>] [peermac1] [peermac2]" + + This command is used to set or get the persistent group record + maintained in the device. Valid index is from 0 to 3. The role should be + 0 for client, 1 for group-owner. + + Example: + ./wifidirectutl <iface> wifidirect_cfg_persistent_group_record 0 1 + 00:50:43:12:24:36 00:50:43:13:26:39 "DIRECT-" "1234567890" + 00:50:43:20:c2:d0 + Set persistent group record with index 0, role as group owner, bssId and + device Id, ssid="DIRECT-", passphrase="1234567890", and peer mac address. + The passphrase get converted to PSK. + + ./wifidirectutl <iface> wifidirect_cfg_persistent_group_record 1 1 + 00:50:43:12:24:36 00:50:43:13:26:39 "DIRECT-" "1234567890" + 0x1234567890123456789012345678901234567890123456789012345678901234 + Set persistent group record with index 1, role as group owner, bssId and + device Id, ssid="DIRECT-", passphrase="1234567890", and peer mac address. + The passphrase get converted to PSK. + PSK is specified with "0x" prefix and 32 bytes (64 characters). + + ./wifidirectutl <iface> wifidirect_cfg_persistent_group_record 1 + Get persistent group record with index 1. + + ./wifidirectutl <iface> wifidirect_cfg_persistent_group_record + Get persistent group record for all indices. + +wifidirect_cfg_persistent_group_invoke +---------- + "./wifidirectutl <iface> wifidirect_cfg_persistent_group_invoke [index] | <cancel>" + + This command is used to invoke a particular persistent group record + from the list. Valid index is from 0 to 3. + + ./wifidirectutl <iface> wifidirect_cfg_persistent_group_invoke 2 + Invoke persistent group record with index 1. + + ./wifidirectutl <iface> wifidirect_cfg_persistent_group_invoke cancel + Cancel invokation of persistent groups. + +wifidirect_cfg_presence_req_params +---------- + "./wifidirectutl <iface> wifidirect_cfg_presence_req_params [<type> <duration> <interval>]" + + This command is used to set/get presence request parameters. Type should + be 1 for preferred values and 2 for acceptable values. + + ./wifidirectutl <iface> wifidirect_cfg_presence_req_params 1 50 100 + Set duration to 50ms and interval to 100ms. + + ./wifidirectutl <iface> wifidirect_cfg_presence_req_params + Get presence request parameters. + +wifidirect_cfg_ext_listen_time +---------- + "./wifidirectutl <iface> wifidirect_cfg_ext_listen_time [<duration> <interval>]" + + This command is used to set/get extended listen timing parameters. + + ./wifidirectutl <iface> wifidirect_cfg_ext_listen_time 1200 1300 + Set availability period to 1200ms and availability interval to 1300 ms. + + ./wifidirectutl <iface> wifidirect_cfg_ext_listen_time + Get extended listen timing parameters. + +wifidirect_cfg_provisioning_params +---------- + "./wifidirectutl <iface> wifidirect_cfg_provisioning_params [<action> <configMethod> <devicePassword>]" + + This command is used to set/get provisioning protocol parameters. Action should + be 1 for request parameters and 2 for response parameters. + + ./wifidirectutl <iface> wifidirect_cfg_provisioning_params 1 0x80 0x04 + Set config method to 0x86 and device password to 0x04. + + ./wifidirectutl <iface> wifidirect_cfg_provisioning_params + Get Provision protocol parameters. + +wifidirect_cfg_wps_params +---------- + "./wifidirectutl <iface> wifidirect_cfg_wps_params [<action>]" + + This command is used to set WPS action. action can be "pin" (pin entered), + "pbc"(button pressed) or "none". + + Example: + ./wifidirectutl <iface> wifidirect_cfg_wps_params pin + Indicates pin operation + + ./wifidirectutl <iface> wifidirect_cfg_wps_params none + Indicates no operation + +=============================================================================== + U S E R M A N U A L F O R WIFIDISPLAY + +wifidisplay_mode +----------- + "./wifidirectutl <iface> wifidisplay_mode [<action>]" + This command is used to enable or disable wifi-display. The possible values are either 1 or 0. + Example: + ./wifidirectutl <iface> wifidisplay_mode 1 + Indicates enable wifi_display + + ./wifidirectutl <iface> wifidisplay_mode 0 + Indicates disable wifi_display + +wifidisplay_config +----------- + "./wifidirectutl <iface> wifidisplay_config [*.conf]" + + This command is used to set/get the wifidisplay configuration. + + Example: + ./wifidirectutl <iface> wifidisplay_config wifidisplay.conf + Read configuration from wifidisplay.conf and apply it. + ./wifidirectutl <iface> wifidisplay_config + Read existing wifidisplay configuration and display it. + +wifidisplay_update_devinfo +---------- + "./wifidirectutl <iface> wifidisplay_update_devinfo [value]" + + This command is used to set the device information of wifidisplay device information subelement. + This command will overwrite the new device information with user defined value. + + Example: + ./wifidirectutl <iface> wifidisplay_update_devinfo 10 + Update device information programmed with new value 10 by overwritting existing value. + +wifidisplay_discovery_request +---------- + "./wifidirectutl <iface> wifidisplay_discovery_request [*.config]" + + This command is used to send wifi display service discovery request. + + Example: + ./wifidirectutl <iface> wifidisplay_discovery_request wifidisplay.conf + Read discovery packet from wifidisplay.conf and send to peer. + +wifidisplay_discovery_response +---------- + "./wifidirectutl <iface> wifidisplay_discovery_response <*.conf>" + + This command is used to send wifidisplay discovery response packet. + + Example: + ./wifidirectutl <iface> wifidisplay_discovery_response wifidisplay.conf + Read discovery packet from wifidisplay.conf and send to peer. + +=============================================================================== + U S E R M A N U A L F O R MLANEVENT + +NAME +mlanevent.exe + +This tool can be used to listen for and obtain events from the driver +through the netlink layer. This is only used for display/debugging purpose. + +---------------- +Supported events +---------------- +WIFIDIRECT_GENERIC_EVENT +WIFIDIRECT_SERVICE_DISCOVERY + +----------------- +Details of events +----------------- + +WIFIDIRECT_GENERIC_EVENT +----------------- + For this event, the following information is shown: + + Event length. + + Event Type indicating Negociation, Invitation, Discoverability, + Provision discovery related Request or Response. + + Event SubType indicating Client or Group owner role. + + Peer MAC address. + + Associated WIFIDIRECT IE elements. + +WIFIDIRECT_SERVICE_DISCOVERY +--------------------- + For this event, the following information is shown: + + Peer MAC address. + + Service discovery packet details.
diff --git a/wlan_sd8897/gpl-2.0.txt b/wlan_sd8897/gpl-2.0.txt new file mode 100644 index 0000000..2c62266 --- /dev/null +++ b/wlan_sd8897/gpl-2.0.txt
@@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your + freedom to share and change it. By contrast, the GNU General Public + License is intended to guarantee your freedom to share and change free + software--to make sure the software is free for all its users. This + General Public License applies to most of the Free Software + Foundation's software and to any other program whose authors commit to + using it. (Some other Free Software Foundation software is covered by + the GNU Lesser General Public License instead.) You can apply it to + your programs, too. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + this service if you wish), that you receive source code or can get it + if you want it, that you can change the software or use pieces of it + in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid + anyone to deny you these rights or to ask you to surrender the rights. + These restrictions translate to certain responsibilities for you if you + distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether + gratis or for a fee, you must give the recipients all the rights that + you have. You must make sure that they, too, receive or can get the + source code. And you must show them these terms so they know their + rights. + + We protect your rights with two steps: (1) copyright the software, and + (2) offer you this license which gives you legal permission to copy, + distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain + that everyone understands that there is no warranty for this free + software. If the software is modified by someone else and passed on, we + want its recipients to know that what they have is not the original, so + that any problems introduced by others will not reflect on the original + authors' reputations. + + Finally, any free program is threatened constantly by software + patents. We wish to avoid the danger that redistributors of a free + program will individually obtain patent licenses, in effect making the + program proprietary. To prevent this, we have made it clear that any + patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and + modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains + a notice placed by the copyright holder saying it may be distributed + under the terms of this General Public License. The "Program", below, + refers to any such program or work, and a "work based on the Program" + means either the Program or any derivative work under copyright law: + that is to say, a work containing the Program or a portion of it, + either verbatim or with modifications and/or translated into another + language. (Hereinafter, translation is included without limitation in + the term "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of + running the Program is not restricted, and the output from the Program + is covered only if its contents constitute a work based on the + Program (independent of having been made by running the Program). + Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's + source code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an appropriate + copyright notice and disclaimer of warranty; keep intact all the + notices that refer to this License and to the absence of any warranty; + and give any other recipients of the Program a copy of this License + along with the Program. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion + of it, thus forming a work based on the Program, and copy and + distribute such modifications or work under the terms of Section 1 + above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, + and can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based + on the Program, the distribution of the whole must be on the terms of + this License, whose permissions for other licensees extend to the + entire whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of + a storage or distribution medium does not bring the other work under + the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source + code means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to + control compilation and installation of the executable. However, as a + special exception, the source code distributed need not include + anything that is normally distributed (in either source or binary + form) with the major components (compiler, kernel, and so on) of the + operating system on which the executable runs, unless that component + itself accompanies the executable. + + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are not + compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program is + void, and will automatically terminate your rights under this License. + However, parties who have received copies, or rights, from you under + this License will not have their licenses terminated so long as such + parties remain in full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Program or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Program (or any work based on the + Program), you indicate your acceptance of this License to do so, and + all its terms and conditions for copying, distributing or modifying + the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further + restrictions on the recipients' exercise of the rights granted herein. + You are not responsible for enforcing compliance by third parties to + this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot + distribute so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you + may not distribute the Program at all. For example, if a patent + license would not permit royalty-free redistribution of the Program by + all those who receive copies directly or indirectly through you, then + the only way you could satisfy both it and this License would be to + refrain entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is + implemented by public license practices. Many people have made + generous contributions to the wide range of software distributed + through that system in reliance on consistent application of that + system; it is up to the author/donor to decide if he or she is willing + to distribute software through any other system and a licensee cannot + impose that choice. + + This section is intended to make thoroughly clear what is believed to + be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License + may add an explicit geographical distribution limitation excluding + those countries, so that distribution is permitted only in or among + countries not thus excluded. In such case, this License incorporates + the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions + of the General Public License from time to time. Such new versions will + be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and conditions + either of that version or of any later version published by the Free + Software Foundation. If the Program does not specify a version number of + this License, you may choose any version ever published by the Free Software + Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the author + to ask for permission. For software which is copyrighted by the Free + Software Foundation, write to the Free Software Foundation; we sometimes + make exceptions for this. Our decision will be guided by the two goals + of preserving the free status of all derivatives of our free software and + of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY + FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN + OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES + PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS + TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, + REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR + REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, + INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING + OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED + TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY + YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER + PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest + to attach them to the start of each source file to most effectively + convey the exclusion of warranty; and each file should have at least + the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + Also add information on how to contact you by electronic and paper mail. + + If the program is interactive, make it output a short notice like this + when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the appropriate + parts of the General Public License. Of course, the commands you use may + be called something other than `show w' and `show c'; they could even be + mouse-clicks or menu items--whatever suits your program. + + You should also get your employer (if you work as a programmer) or your + school, if any, to sign a "copyright disclaimer" for the program, if + necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + + This General Public License does not permit incorporating your program into + proprietary programs. If your program is a subroutine library, you may + consider it more useful to permit linking proprietary applications with the + library. If this is what you want to do, use the GNU Lesser General + Public License instead of this License.
diff --git a/wlan_sd8897/mapp/mlan2040coex/Makefile b/wlan_sd8897/mapp/mlan2040coex/Makefile new file mode 100644 index 0000000..0d94daf --- /dev/null +++ b/wlan_sd8897/mapp/mlan2040coex/Makefile
@@ -0,0 +1,48 @@ +# +# File : mlan2040coex/Makefile +# +# Copyright (C) 2009-2017, Marvell International Ltd. All Rights Reserved + +# Path to the top directory of the mlandriver distribution +PATH_TO_TOP = ../.. + +# Determine how we should copy things to the install directory +ABSPATH := $(filter /%, $(INSTALLDIR)) +RELPATH := $(filter-out /%, $(INSTALLDIR)) +INSTALLPATH := $(ABSPATH) +ifeq ($(strip $(INSTALLPATH)),) +INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH) +endif + +# Override CFLAGS for application sources, remove __ kernel namespace defines +CFLAGS := $(filter-out -D__%, $(ccflags-y)) +# remove KERNEL include dir +CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS)) + +# +# List of application executables to create +# +libobjs:= mlan2040coex.o mlan2040misc.o +exectarget=mlan2040coex +TARGETS := $(exectarget) + +# +# Make target rules +# + +# All rule compiles list of TARGETS using builtin program target from src rule +all : +$(exectarget): $(libobjs) + $(CC) $(CFLAGS) $(libobjs) -o $(exectarget) + +# Update any needed TARGETS and then copy to the install path +build install: $(TARGETS) + @cp -f $(exectarget) $(INSTALLPATH) + +clean: + @rm -f $(exectarget) + @rm -f *.o + +distclean: clean + @rm -f *~ core + @rm -f tags
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.c b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.c new file mode 100644 index 0000000..aa2703b --- /dev/null +++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.c
@@ -0,0 +1,1323 @@ +/** @file mlan2040coex.c + * + * @brief 11n 20/40 MHz Coex application + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 06/24/2009: initial version +************************************************************************/ + +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <linux/if.h> +#include <linux/wireless.h> +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#ifdef ANDROID +#include <net/if_ether.h> +#else +#include <net/ethernet.h> +#endif +#include "mlan2040coex.h" +#include "mlan2040misc.h" + +/** coex application's version number */ +#define COEX_VER "M2.0" + +/** Initial number of total private ioctl calls */ +#define IW_INIT_PRIV_NUM 128 +/** Maximum number of total private ioctl calls supported */ +#define IW_MAX_PRIV_NUM 1024 + +/******************************************************** + Local Variables +********************************************************/ + +static char *usage[] = { + "Usage: ", + " mlan2040coex [-i <intfname>] [-hvB] ", + " -h = help", + " -v = version", + " -B = run the process in background.", + " (if intfname not present then mlan0 assumed)" +}; + +t_s32 sockfd = 0; /**< socket descriptor */ +char dev_name[IFNAMSIZ + 1]; /**< device name */ + +/** Flag: is 2040coex command required */ +int coex_cmd_req_flag = FALSE; +/** Flag: is associated */ +int assoc_flag = FALSE; +/** Flag: is HT AP */ +int is_ht_ap = FALSE; +/** terminate flag */ +int terminate_flag = FALSE; + +/******************************************************** + Global Variables +********************************************************/ +/** OBSS scan parameter of associated AP */ +OBSSScanParam_t cur_obss_scan_param; + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Prepare command buffer + * @param buffer Command buffer to be filled + * @param cmd Command id + * @param num Number of arguments + * @param args Arguments list + * @return MLAN_STATUS_SUCCESS + */ +static int +prepare_buffer(t_u8 *buffer, char *cmd, t_u32 num, char *args[]) +{ + t_u8 *pos = NULL; + unsigned int i = 0; + + memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + + /* Flag it for our use */ + pos = buffer; + strncpy((char *)pos, CMD_MARVELL, strlen(CMD_MARVELL)); + pos += (strlen(CMD_MARVELL)); + + /* Insert command */ + strncpy((char *)pos, (char *)cmd, strlen(cmd)); + pos += (strlen(cmd)); + + /* Insert arguments */ + for (i = 0; i < num; i++) { + strncpy((char *)pos, args[i], strlen(args[i])); + pos += strlen(args[i]); + if (i < (num - 1)) { + strncpy((char *)pos, " ", strlen(" ")); + pos += 1; + } + } + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process OBSS scan table + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_scantable(void) +{ + char ssid[MRVDRV_MAX_SSID_LENGTH + 1] = { 0 }; + unsigned int scan_start; + int idx, i = 0, j, already_listed, ssid_len = 0, ssid_idx; + + t_u8 *pcurrent; + t_u8 *pnext; + IEEEtypes_ElementId_e *pelement_id; + t_u8 *pelement_len, ht_cap_present, intol_bit_is_set; + int ret = MLAN_STATUS_SUCCESS; + t_s32 bss_info_len = 0; + t_u32 fixed_field_length = 0; + + IEEEtypes_CapInfo_t cap_info; + t_u8 tsf[8]; + t_u16 beacon_interval; + IEEEtypes_HTCap_t *pht_cap; + + wlan_ioctl_get_scan_table_info *prsp_info; + wlan_get_scan_table_fixed fixed_fields; + + t_u8 *buffer = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + + /* Start preparing the buffer */ + /* Initialize buffer */ + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + memset(&cap_info, 0x00, sizeof(cap_info)); + memset(leg_ap_chan_list, 0, sizeof(leg_ap_chan_list)); + num_leg_ap_chan = 0; + + scan_start = 1; + + do { + /* buffer = CMD_MARVELL + <cmd_string> */ + prepare_buffer(buffer, "getscantable", 0, NULL); + prsp_info = + (wlan_ioctl_get_scan_table_info *)(buffer + + strlen(CMD_MARVELL) + + strlen + ("getscantable")); + + prsp_info->scan_number = scan_start; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + if (errno == EAGAIN) { + ret = -EAGAIN; + } else { + perror("mlan2040coex"); + fprintf(stderr, + "mlan2040coex: getscantable fail\n"); + ret = MLAN_STATUS_FAILURE; + } + goto done; + } + + prsp_info = (wlan_ioctl_get_scan_table_info *)buffer; + pcurrent = 0; + pnext = prsp_info->scan_table_entry_buf; + + if (scan_start == 1) { + printf("----------------------------------------------\n"); + } + + for (idx = 0; (unsigned int)idx < prsp_info->scan_number; idx++) { + + /* + * Set pcurrent to pnext in case pad bytes are at the end + * of the last IE we processed. + */ + pcurrent = pnext; + + memcpy((t_u8 *)&fixed_field_length, + (t_u8 *)pcurrent, sizeof(fixed_field_length)); + pcurrent += sizeof(fixed_field_length); + + memcpy((t_u8 *)&bss_info_len, + (t_u8 *)pcurrent, sizeof(bss_info_len)); + pcurrent += sizeof(bss_info_len); + + memcpy((t_u8 *)&fixed_fields, + (t_u8 *)pcurrent, sizeof(fixed_fields)); + pcurrent += fixed_field_length; + + pnext = pcurrent + bss_info_len; + + if (bss_info_len >= (sizeof(tsf) + + sizeof(beacon_interval) + + sizeof(cap_info))) { + pcurrent += + (sizeof(tsf) + sizeof(beacon_interval) + + sizeof(cap_info)); + bss_info_len -= + (sizeof(tsf) + sizeof(beacon_interval) + + sizeof(cap_info)); + } + ht_cap_present = FALSE; + intol_bit_is_set = FALSE; + memset(ssid, 0, MRVDRV_MAX_SSID_LENGTH + 1); + ssid_len = 0; + while (bss_info_len >= 2) { + pelement_id = (IEEEtypes_ElementId_e *)pcurrent; + pelement_len = pcurrent + 1; + pcurrent += 2; + + switch (*pelement_id) { + case SSID: + if (*pelement_len && + *pelement_len <= + MRVDRV_MAX_SSID_LENGTH) { + memcpy(ssid, pcurrent, + *pelement_len); + ssid_len = *pelement_len; + } + break; + + case HT_CAPABILITY: + pht_cap = + (IEEEtypes_HTCap_t *) + pelement_id; + ht_cap_present = TRUE; + if (IS_INTOL_BIT_SET + (le16_to_cpu + (pht_cap->ht_cap.ht_cap_info))) { + intol_bit_is_set = TRUE; + } + break; + default: + break; + } + pcurrent += *pelement_len; + bss_info_len -= (2 + *pelement_len); + } + if (!ht_cap_present || intol_bit_is_set) { + printf("%s AP found on channel number: %-3d ", + intol_bit_is_set ? "40 MHZ intolerant" : + "Legacy", fixed_fields.channel); + if (ssid_len) { + printf("SSID: "); + /* Print out the ssid or the hex values if non-printable */ + for (ssid_idx = 0; ssid_idx < ssid_len; + ssid_idx++) { + if (isprint(ssid[ssid_idx])) { + printf("%c", + ssid[ssid_idx]); + } else { + printf("\\%02x", + ssid[ssid_idx]); + } + } + } + printf("\n"); + + /* Verify that the channel is already listed or not */ + already_listed = FALSE; + for (j = 0; j < i; j++) { + if (leg_ap_chan_list[j].chan_num == + fixed_fields.channel) { + already_listed = TRUE; + if (intol_bit_is_set) + leg_ap_chan_list[j]. + is_intol_set = + intol_bit_is_set; + break; + } + } + if (!already_listed) { + /* add the channel in list */ + leg_ap_chan_list[i].chan_num = + fixed_fields.channel; + leg_ap_chan_list[i].is_intol_set = + intol_bit_is_set; + i++; + coex_cmd_req_flag = TRUE; + num_leg_ap_chan++; + } + } + } + scan_start += prsp_info->scan_number; + + } while (prsp_info->scan_number); + +done: + if (cmd) + free(cmd); + if (buffer) + free(buffer); + return ret; +} + +/** BSS Mode any (both infra and adhoc) */ +#define BSS_MODE_ANY 3 + +/** current scan config parameters */ +#define SCAN_CFG_PARAMS 7 + +/** Active : 1 , Passive : 2 */ +#define SCAN_TYPE_ACTIVE 1 + +/** + * @brief Issue get scan type command + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +get_scan_cfg(int *scan_param) +{ + t_u8 *buffer = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + int ret = MLAN_STATUS_SUCCESS; + + /* Start preparing the buffer */ + /* Initialize buffer */ + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + return MLAN_STATUS_FAILURE; + } + + /* buffer = CMD_MARVELL + <cmd_string> */ + prepare_buffer(buffer, "scancfg", 0, NULL); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("mlan2040coex"); + fprintf(stderr, "mlan2040coex: get_scan_cfg fail\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan_param = (int *)(buffer); +done: + if (cmd) + free(cmd); + if (buffer) + free(buffer); + + return ret; +} + +/** + * @brief Issue OBSS scan command + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_setuserscan(void) +{ + wlan_ioctl_user_scan_cfg scan_req; + int scan_cfg[SCAN_CFG_PARAMS]; + t_u8 *buffer = NULL, *pos = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + int status = 0; + + memset(&scan_req, 0x00, sizeof(scan_req)); + memset(scan_cfg, 0x00, SCAN_CFG_PARAMS); + coex_cmd_req_flag = FALSE; + + if (get_scan_cfg(scan_cfg) != MLAN_STATUS_SUCCESS) { + printf("mlan2040coex: scancfg ioctl failure"); + return -EFAULT; + } + + if (scan_cfg[0] == SCAN_TYPE_ACTIVE) + scan_req.chan_list[0].scan_time = + (t_u32)le16_to_cpu(cur_obss_scan_param. + obss_scan_active_dwell); + else + scan_req.chan_list[0].scan_time = + (t_u32)le16_to_cpu(cur_obss_scan_param. + obss_scan_passive_total); + scan_req.bss_mode = (scan_cfg[1]) ? scan_cfg[1] : BSS_MODE_ANY; + + /* Start preparing the buffer */ + /* Initialize buffer */ + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + return MLAN_STATUS_FAILURE; + } + + /* buffer = CMD_MARVELL + <cmd_string> */ + prepare_buffer(buffer, "setuserscan", 0, NULL); + pos = buffer + strlen(CMD_MARVELL) + strlen("setuserscan"); + + /* buffer = buffer + 'scan_req' */ + memcpy(pos, &scan_req, sizeof(wlan_ioctl_user_scan_cfg)); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + free(buffer); + return MLAN_STATUS_FAILURE; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("mlan2040coex"); + fprintf(stderr, "mlan2040coex: setuserscan fail\n"); + if (cmd) + free(cmd); + if (buffer) + free(buffer); + return MLAN_STATUS_FAILURE; + } + + /** process scan results */ + do { + status = process_scantable(); + } while (status == -EAGAIN); + + if (cmd) + free(cmd); + if (buffer) + free(buffer); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Display usage + * + * @return NA + */ +static t_void +display_usage(t_void) +{ + t_u32 i; + for (i = 0; i < NELEMENTS(usage); i++) + fprintf(stderr, "%s\n", usage[i]); +} + +/** + * @brief get connection status + * + * @param data Pointer to the output buffer holding connection status + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +get_connstatus(int *data) +{ + struct ether_addr apaddr; + struct ether_addr etherzero = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; + t_u8 *buffer = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + + /* Initialize buffer */ + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + return MLAN_STATUS_FAILURE; + } + + /* buffer = CMD_MARVELL + <cmd_string> */ + prepare_buffer(buffer, "getwap", 0, NULL); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + free(buffer); + return MLAN_STATUS_FAILURE; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("mlan2040coex"); + fprintf(stderr, "mlan2040coex: getwap fail\n"); + if (cmd) + free(cmd); + if (buffer) + free(buffer); + return MLAN_STATUS_FAILURE; + } + + memset(&apaddr, 0, sizeof(struct ether_addr)); + memcpy(&apaddr, (struct ether_addr *)(buffer), + sizeof(struct ether_addr)); + + if (!memcmp(&apaddr, ðerzero, sizeof(struct ether_addr))) { + /* not associated */ + *data = FALSE; + } else { + /* associated */ + *data = TRUE; + } + + if (buffer) + free(buffer); + if (cmd) + free(cmd); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Print connect and disconnect event related information + * + * @param buffer Pointer to received event buffer + * @param size Length of the received event + * + * @return N/A + */ +void +print_event_drv_connected(t_u8 *buffer, t_u16 size) +{ + struct ether_addr *wap; + struct ether_addr etherzero = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; + char buf[32]; + + wap = (struct ether_addr *)(buffer + strlen(CUS_EVT_AP_CONNECTED)); + + if (!memcmp(wap, ðerzero, sizeof(struct ether_addr))) { + printf("---< Disconnected from AP >---\n"); + memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param)); + assoc_flag = FALSE; + is_ht_ap = FALSE; + } else { + memset(buf, 0, sizeof(buf)); + snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", + wap->ether_addr_octet[0], + wap->ether_addr_octet[1], + wap->ether_addr_octet[2], + wap->ether_addr_octet[3], + wap->ether_addr_octet[4], wap->ether_addr_octet[5]); + printf("---< Connected to AP: %s >---\n", buf); + /** set TRUE, if connected */ + assoc_flag = TRUE; + } +} + +/** + * @brief Parse and print received event information + * + * @param event Pointer to received event + * @param size Length of the received event + * @param evt_conn A pointer to a output buffer. It sets TRUE when it gets + * the event CUS_EVT_OBSS_SCAN_PARAM, otherwise FALSE + * @param if_name The interface name + * + * @return N/A + */ +void +print_event(event_header *event, t_u16 size, int *evt_conn, char *if_name) +{ + if (!strncmp + (CUS_EVT_AP_CONNECTED, (char *)event, + strlen(CUS_EVT_AP_CONNECTED))) { + if (strlen(if_name)) + printf("EVENT for interface %s\n", if_name); + print_event_drv_connected((t_u8 *)event, size); + return; + } + if (!strncmp + (CUS_EVT_OBSS_SCAN_PARAM, (char *)event, + strlen(CUS_EVT_OBSS_SCAN_PARAM))) { + if (strlen(if_name)) + printf("EVENT for interface %s\n", if_name); + printf("---< %s >---\n", CUS_EVT_OBSS_SCAN_PARAM); + memset(&cur_obss_scan_param, 0, sizeof(cur_obss_scan_param)); + memcpy(&cur_obss_scan_param, + ((t_u8 *)event + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1), + sizeof(cur_obss_scan_param)); + /** set TRUE, since it is an HT AP */ + is_ht_ap = TRUE; + *evt_conn = TRUE; + return; + } + if (!strncmp + (CUS_EVT_BW_CHANGED, (char *)event, strlen(CUS_EVT_BW_CHANGED))) { + if (strlen(if_name)) + printf("EVENT for interface %s\n", if_name); + printf("---< %s >---\n", CUS_EVT_BW_CHANGED); + return; + } +} + +/** + * @brief This function parses for NETLINK events + * + * @param nlh Pointer to Netlink message header + * @param bytes_read Number of bytes to be read + * @param evt_conn A pointer to a output buffer. It sets TRUE when it gets + * the event CUS_EVT_OBSS_SCAN_PARAM, otherwise FALSE + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int +drv_nlevt_handler(struct nlmsghdr *nlh, int bytes_read, int *evt_conn) +{ + int len, plen; + t_u8 *buffer = NULL; + t_u32 event_id = 0; + event_header *event = NULL; + char if_name[IFNAMSIZ + 1]; + + /* Initialize receive buffer */ + buffer = (t_u8 *)malloc(NL_MAX_PAYLOAD); + if (!buffer) { + printf("ERR: Could not alloc buffer\n"); + return MLAN_STATUS_FAILURE; + } + memset(buffer, 0, NL_MAX_PAYLOAD); + + *evt_conn = FALSE; + while ((unsigned int)bytes_read >= NLMSG_HDRLEN) { + len = nlh->nlmsg_len; /* Length of message including header */ + plen = len - NLMSG_HDRLEN; + if (len > bytes_read || plen < 0) { + /* malformed netlink message */ + return MLAN_STATUS_FAILURE; + } + if ((unsigned int)len > NLMSG_SPACE(NL_MAX_PAYLOAD)) { + printf("ERR:Buffer overflow!\n"); + return MLAN_STATUS_FAILURE; + } + memset(buffer, 0, NL_MAX_PAYLOAD); + memcpy(buffer, NLMSG_DATA(nlh), plen); + + if (NLMSG_OK(nlh, len)) { + memcpy(&event_id, buffer, sizeof(event_id)); + + if (((event_id & 0xFF000000) == 0x80000000) || + ((event_id & 0xFF000000) == 0)) { + event = (event_header *)buffer; + } else { + memset(if_name, 0, IFNAMSIZ + 1); + memcpy(if_name, buffer, IFNAMSIZ); + event = (event_header *)(buffer + IFNAMSIZ); + } + } + + print_event(event, bytes_read, evt_conn, if_name); + len = NLMSG_ALIGN(len); + bytes_read -= len; + nlh = (struct nlmsghdr *)((char *)nlh + len); + } + return MLAN_STATUS_SUCCESS; +} + +/** Maximum event message length */ +#define MAX_MSG_LENGTH 1024 + +/** + * @brief Configure and read event data from netlink socket + * + * @param nl_sk Netlink socket handler + * @param msg Pointer to message header + * @param ptv Pointer to struct timeval + * + * @return Number of bytes read or MLAN_STATUS_FAILURE + */ +int +read_event(int nl_sk, struct msghdr *msg, struct timeval *ptv) +{ + int count = -1; + int ret = MLAN_STATUS_FAILURE; + fd_set rfds; + + /* Setup read fds and initialize event buffers */ + FD_ZERO(&rfds); + FD_SET(nl_sk, &rfds); + + /* Wait for reply */ + ret = select(nl_sk + 1, &rfds, NULL, NULL, ptv); + + if (ret == MLAN_STATUS_FAILURE) { + /* Error */ + terminate_flag++; + ptv->tv_sec = DEFAULT_SCAN_INTERVAL; + ptv->tv_usec = 0; + goto done; + } else if (!ret) { + if (assoc_flag && is_ht_ap) { + /** Issue OBSS scan */ + process_setuserscan(); + /** Invoke 2040coex command, if any legacy AP found or + * any AP has 40MHz intolarent bit set */ + if (coex_cmd_req_flag) + invoke_coex_command(); + } + if (assoc_flag && is_ht_ap) { + /* Timeout. Try again after BSS channel width triger scan + interval when the STA is connected with a HT AP */ + ptv->tv_sec = + (t_u32)le16_to_cpu(cur_obss_scan_param. + bss_chan_width_trigger_scan_int); + } else { + /* Timeout. Try again after default duration when the STA is + not connected with a HT AP */ + ptv->tv_sec = DEFAULT_SCAN_INTERVAL; + } + ptv->tv_usec = 0; + goto done; + } + + if (!FD_ISSET(nl_sk, &rfds)) { + /* Unexpected error. Try again */ + ptv->tv_sec = DEFAULT_SCAN_INTERVAL; + ptv->tv_usec = 0; + goto done; + } + /* Success */ + count = recvmsg(nl_sk, msg, 0); + +done: + return count; +} + +/** Maximum event message length */ +#define MAX_MSG_LENGTH 1024 +/** + * @brief Run the application + * + * @param nl_sk Netlink socket + * + * @return N/A + */ +void +run_app(int nl_sk) +{ + struct timeval tv; + int bytes_read, evt_conn; + struct msghdr msg; + struct sockaddr_nl dest_addr; + struct nlmsghdr *nlh; + struct iovec iov; + + /** Get connection status */ + if (get_connstatus(&assoc_flag) == MLAN_STATUS_FAILURE) + return; + + /* Initialize timeout value */ + tv.tv_sec = DEFAULT_SCAN_INTERVAL; + tv.tv_usec = 0; + + /* Initialize netlink header */ + nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(NL_MAX_PAYLOAD)); + if (!nlh) { + printf("ERR: Could not allocate space for netlink header\n"); + goto done; + } + memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD)); + /* Fill the netlink message header */ + nlh->nlmsg_len = NLMSG_SPACE(NL_MAX_PAYLOAD); + nlh->nlmsg_pid = getpid(); /* self pid */ + nlh->nlmsg_flags = 0; + + /* Initialize I/O vector */ + memset(&iov, 0, sizeof(struct iovec)); + iov.iov_base = (void *)nlh; + iov.iov_len = nlh->nlmsg_len; + + /* Set destination address */ + memset(&dest_addr, 0, sizeof(struct sockaddr_nl)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; /* Kernel */ + dest_addr.nl_groups = NL_MULTICAST_GROUP; + + /* Initialize message header */ + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + while (!terminate_flag) { + /* event buffer is received for all the interfaces */ + bytes_read = read_event(nl_sk, &msg, &tv); + /* handle only NETLINK events here */ + drv_nlevt_handler((struct nlmsghdr *)nlh, bytes_read, + &evt_conn); + if (assoc_flag && is_ht_ap) { + /** If the event is connected with an HT AP then issue OBSS scan immediately */ + if (evt_conn) { + /** Issue OBSS scan */ + process_setuserscan(); + /** Invoke 2040coex command, if any legacy AP found or + * any AP has 40MHz intolarent bit set */ + if (coex_cmd_req_flag) + invoke_coex_command(); + } + tv.tv_sec = + (t_u32)le16_to_cpu(cur_obss_scan_param. + bss_chan_width_trigger_scan_int); + } else { + tv.tv_sec = DEFAULT_SCAN_INTERVAL; + } + tv.tv_usec = 0; + } + +done: + if (nl_sk > 0) + close(nl_sk); + if (nlh) + free(nlh); + return; +} + +/** + * @brief Determine the netlink number + * + * @return Netlink number to use + */ +static int +get_netlink_num(int dev_index) +{ + FILE *fp = NULL; + int netlink_num = NETLINK_MARVELL; + char str[64]; + char *srch = "netlink_num"; + char filename[64]; + + if (dev_index == 0) { + strcpy(filename, "/proc/mwlan/config"); + } else if (dev_index > 0) { + sprintf(filename, "/proc/mwlan/config%d", dev_index); + } + /* Try to open /proc/mwlan/config$ */ + fp = fopen(filename, "r"); + + if (fp) { + while (!feof(fp)) { + fgets(str, sizeof(str), fp); + if (strncmp(str, srch, strlen(srch)) == 0) { + netlink_num = atoi(str + strlen(srch) + 1); + break; + } + } + fclose(fp); + } else { + return -1; + } + + printf("Netlink number = %d\n", netlink_num); + return netlink_num; +} + +/** + * @brief opens netlink socket to receive NETLINK events + * @return socket id --success, otherwise--MLAN_STATUS_FAILURE + */ +int +open_netlink(int dev_index) +{ + int sk = -1; + struct sockaddr_nl src_addr; + int netlink_num = 0; + + netlink_num = get_netlink_num(dev_index); + if (netlink_num < 0) { + printf("ERR:Could not get netlink socket. Invalid device number.\n"); + return sk; + } + + /* Open netlink socket */ + sk = socket(PF_NETLINK, SOCK_RAW, netlink_num); + if (sk < 0) { + printf("ERR:Could not open netlink socket.\n"); + return sk; + } + + /* Set source address */ + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); + src_addr.nl_groups = NL_MULTICAST_GROUP; + + /* Bind socket with source address */ + if (bind(sk, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) { + printf("ERR:Could not bind socket!\n"); + close(sk); + return -1; + } + return sk; +} + +/** + * @brief Terminate signal handler + * @param signal Signal to handle + * @return NA + */ +static t_void +terminate_handler(int signal) +{ + printf("Stopping application.\n"); +#if DEBUG + printf("Process ID of process killed = %d\n", getpid()); +#endif + terminate_flag = TRUE; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Process host command + * @param hostcmd_idx Host command index + * @param chan_list A pointer to a channel list + * @param chan_num Number of channels in the channel list + * @param reg_class Regulatory class of the channels + * @param is_intol_ap_present Flag:is there any 40 MHz intolerant AP is present or not + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_host_cmd(int hostcmd_idx, t_u8 *chan_list, t_u8 chan_num, + t_u8 reg_class, t_u8 is_intol_ap_present) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 *buffer = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + prepare_buffer(buffer, "hostcmd", 0, NULL); + switch (hostcmd_idx) { + case CMD_2040COEX: + prepare_coex_cmd_buff(buffer + strlen(CMD_MARVELL) + + strlen("hostcmd"), chan_list, chan_num, + reg_class, is_intol_ap_present); + break; + default: + break; + } + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("mlan2040coex"); + fprintf(stderr, "mlan2040coex: hostcmd fail\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = process_host_cmd_resp("hostcmd", buffer); + +done: + if (cmd) + free(cmd); + if (buffer) + free(buffer); + return ret; +} + +/** + * @brief Check the STA is 40 MHz intolerant or not + * @param intol Flag: TRUE when the STA is 40 MHz intolerant, otherwise FALSE + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +is_intolerant_sta(int *intol) +{ + t_u8 *buffer = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + int htcap_info, ret = MLAN_STATUS_SUCCESS; + + *intol = FALSE; + + /* Initialize buffer */ + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* buffer = CMD_MARVELL + <cmd_string> */ + prepare_buffer(buffer, "htcapinfo", 0, NULL); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("mlan2040coex"); + fprintf(stderr, "mlan2040coex: htcapinfo fail\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + htcap_info = *((int *)(buffer)); + + if (htcap_info & MBIT(8)) + *intol = TRUE; + +done: + if (cmd) + free(cmd); + if (buffer) + free(buffer); + return ret; +} + +/** + * @brief get region code + * @param reg_code Pointer to region code + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +get_region_code(int *reg_code) +{ + t_u8 *buffer = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + int ret = MLAN_STATUS_SUCCESS; + + /* Initialize buffer */ + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* buffer = CMD_MARVELL + <cmd_string> */ + prepare_buffer(buffer, "regioncode", 0, NULL); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("mlan2040coex"); + fprintf(stderr, "mlan2040coex: regioncode fail\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memcpy(reg_code, buffer, sizeof(int)); +done: + if (cmd) + free(cmd); + if (buffer) + free(buffer); + return ret; +} + +/** No option */ +#define NO_OPTION -1 + +/** + * @brief Entry function for coex + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +main(int argc, char *argv[]) +{ + char ifname[IFNAMSIZ + 1] = "mlan0"; + int c, daemonize = FALSE; + t_s32 nl_sk; + /**< netlink socket descriptor to receive an event */ + int dev_index = 0; /** initialise with -1 to open multiple NETLINK Sockets */ + + char temp[2]; + int arg_count = 0; + int ifname_given = FALSE; + + for (;;) { + c = getopt(argc, argv, "Bhi:vd:"); + /* check if all command-line options have been parsed */ + if (c == NO_OPTION) + break; + + switch (c) { + case 'B': + daemonize = TRUE; + break; + case 'h': + display_usage(); + return MLAN_STATUS_SUCCESS; + case 'v': + fprintf(stdout, + "Marvell 20/40coex application version %s\n", + COEX_VER); + return MLAN_STATUS_SUCCESS; + case 'i': + ifname_given = TRUE; + if (strlen(optarg) < IFNAMSIZ) + strncpy(ifname, optarg, IFNAMSIZ - 1); + else { + fprintf(stdout, "Interface name too long\n"); + display_usage(); + return MLAN_STATUS_SUCCESS; + } + arg_count += 1; + break; + case 'd': + strncpy(temp, optarg, strlen(optarg)); + if (isdigit(temp[0])) + dev_index = atoi(temp); + arg_count += 1; + break; + default: + fprintf(stdout, "Invalid argument\n"); + display_usage(); + return MLAN_STATUS_SUCCESS; + } + } + + if (!ifname_given) { + sprintf(ifname, "mlan%d", dev_index); + } + + if (optind < argc) { + fputs("Too many arguments.\n", stderr); + display_usage(); + goto done; + } + + strncpy(dev_name, ifname, IFNAMSIZ - 1); + + /* create a socket */ + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "mlan2040coex: Cannot open socket.\n"); + goto done; + } + + /* create netlink sockets and bind them with app side addr */ + nl_sk = open_netlink(dev_index); + + if (nl_sk < 0) { + fprintf(stderr, "mlan2040coex: Cannot open netlink socket.\n"); + goto done; + } + + signal(SIGHUP, terminate_handler); /* catch hangup signal */ + signal(SIGTERM, terminate_handler); /* catch kill signal */ + signal(SIGINT, terminate_handler); /* catch kill signal */ + signal(SIGALRM, terminate_handler); /* catch kill signal */ + + /** Make the process background-process */ + if (daemonize) { + if (daemon(0, 0)) + fprintf(stderr, "mlan2040coex: Cannot start daemon\n"); + } + + /** run the application */ + run_app(nl_sk); + +done: + if (sockfd > 0) + close(sockfd); + if (nl_sk > 0) + close(nl_sk); + + return MLAN_STATUS_SUCCESS; +}
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.h b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.h new file mode 100644 index 0000000..01339d8 --- /dev/null +++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040coex.h
@@ -0,0 +1,233 @@ +/** @file mlan2040coex.h + * + * @brief This file contains definitions for application + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 06/24/2009: initial version +************************************************************************/ +#ifndef _COEX_H_ +#define _COEX_H_ + +/** Marvell private command identifier */ +#define CMD_MARVELL "MRVL_CMD" + +/** IOCTL number */ +#define MLAN_ETH_PRIV (SIOCDEVPRIVATE + 14) + +#if (BYTE_ORDER == LITTLE_ENDIAN) +#undef BIG_ENDIAN_SUPPORT +#endif + +/** 16 bits byte swap */ +#define swap_byte_16(x) \ +((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \ + (((t_u16)(x) & 0xff00U) >> 8))) + +/** 32 bits byte swap */ +#define swap_byte_32(x) \ +((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \ + (((t_u32)(x) & 0x0000ff00UL) << 8) | \ + (((t_u32)(x) & 0x00ff0000UL) >> 8) | \ + (((t_u32)(x) & 0xff000000UL) >> 24))) + +/** 64 bits byte swap */ +#define swap_byte_64(x) \ + ((t_u64)((t_u64)(((t_u64)(x) & 0x00000000000000ffULL) << 56) | \ + (t_u64)(((t_u64)(x) & 0x000000000000ff00ULL) << 40) | \ + (t_u64)(((t_u64)(x) & 0x0000000000ff0000ULL) << 24) | \ + (t_u64)(((t_u64)(x) & 0x00000000ff000000ULL) << 8) | \ + (t_u64)(((t_u64)(x) & 0x000000ff00000000ULL) >> 8) | \ + (t_u64)(((t_u64)(x) & 0x0000ff0000000000ULL) >> 24) | \ + (t_u64)(((t_u64)(x) & 0x00ff000000000000ULL) >> 40) | \ + (t_u64)(((t_u64)(x) & 0xff00000000000000ULL) >> 56) )) + +/** Convert to correct endian format */ +#ifdef BIG_ENDIAN_SUPPORT +/** CPU to little-endian convert for 16-bit */ +#define cpu_to_le16(x) swap_byte_16(x) +/** CPU to little-endian convert for 32-bit */ +#define cpu_to_le32(x) swap_byte_32(x) +/** CPU to little-endian convert for 64-bit */ +#define cpu_to_le64(x) swap_byte_64(x) +/** Little-endian to CPU convert for 16-bit */ +#define le16_to_cpu(x) swap_byte_16(x) +/** Little-endian to CPU convert for 32-bit */ +#define le32_to_cpu(x) swap_byte_32(x) +/** Little-endian to CPU convert for 64-bit */ +#define le64_to_cpu(x) swap_byte_64(x) +#else +/** Do nothing */ +#define cpu_to_le16(x) (x) +/** Do nothing */ +#define cpu_to_le32(x) (x) +/** Do nothing */ +#define cpu_to_le64(x) (x) +/** Do nothing */ +#define le16_to_cpu(x) (x) +/** Do nothing */ +#define le32_to_cpu(x) (x) +/** Do nothing */ +#define le64_to_cpu(x) (x) +#endif + +#ifdef __GNUC__ +/** Structure packing begins */ +#define PACK_START +/** Structure packeing end */ +#define PACK_END __attribute__ ((packed)) +#else +/** Structure packing begins */ +#define PACK_START __packed +/** Structure packeing end */ +#define PACK_END +#endif + +/** Character, 1 byte */ +typedef signed char t_s8; +/** Unsigned character, 1 byte */ +typedef unsigned char t_u8; + +/** Short integer */ +typedef signed short t_s16; +/** Unsigned short integer */ +typedef unsigned short t_u16; + +/** Integer */ +typedef signed int t_s32; +/** Unsigned integer */ +typedef unsigned int t_u32; + +/** Long long integer */ +typedef signed long long t_s64; +/** Unsigned long long integer */ +typedef unsigned long long t_u64; + +/** Void pointer (4-bytes) */ +typedef void t_void; + +#ifdef FALSE +#undef FALSE +#endif + +#ifdef TRUE +#undef TRUE +#endif + +#ifndef MIN +/** Find minimum value */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +/** Type definition: boolean */ +typedef enum { FALSE, TRUE } boolean; + +/** Find number of elements */ +#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) +/** Success */ +#define MLAN_STATUS_SUCCESS (0) +/** Failure */ +#define MLAN_STATUS_FAILURE (-1) + +/** Enumeration for host-command index */ +enum COMMANDS { + CMD_2040COEX = 1, +}; +/** Maximum number of channels that can be sent in a setuserscan ioctl */ +#define WLAN_IOCTL_USER_SCAN_CHAN_MAX 50 + +#ifndef ETH_ALEN +/** MAC address length */ +#define ETH_ALEN 6 +#endif + +/** Netlink protocol number */ +#define NETLINK_MARVELL (MAX_LINKS - 1) +/** Netlink maximum payload size */ +#define NL_MAX_PAYLOAD 1024 +/** Netlink multicast group number */ +#define NL_MULTICAST_GROUP RTMGRP_LINK +/** Default wait time in seconds for events */ +#define UAP_RECV_WAIT_DEFAULT 10 +#ifndef NLMSG_HDRLEN +/** NL message header length */ +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#endif + +/** Event header */ +typedef PACK_START struct _event_header { + /** Event ID */ + t_u32 event_id; + /** Event data */ + t_u8 event_data[0]; +} PACK_END event_header; + +/** Event ID length */ +#define EVENT_ID_LEN 4 + +/** Custom events definitions */ +/** AP connected event */ +#define CUS_EVT_AP_CONNECTED "EVENT=AP_CONNECTED" + +/** Custom event : BW changed */ +#define CUS_EVT_BW_CHANGED "EVENT=BW_CHANGED" +/** Custom event : OBSS scan parameter */ +#define CUS_EVT_OBSS_SCAN_PARAM "EVENT=OBSS_SCAN_PARAM" + +/** Custom events definitions end */ + +/** Structure defination of chan_intol_t*/ +typedef struct _chan_intol_t { + /** Channel numer */ + t_u8 chan_num; + /** Flag: Is any 40MHz intolerant AP found in this channel */ + t_u8 is_intol_set; +} chan_intol_t; + +/** Private command structure */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT +struct eth_priv_cmd { + /** Command buffer pointer */ + t_u64 buf; + /** buffer updated by driver */ + int used_len; + /** buffer sent by application */ + int total_len; +} __ATTRIB_PACK__; +#else +struct eth_priv_cmd { + /** Command buffer */ + t_u8 *buf; + /** Used length */ + int used_len; + /** Total length */ + int total_len; +}; +#endif + +/** Legacy APs channel list */ +chan_intol_t leg_ap_chan_list[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; +/** Total number of channel present in Legacy APs channel list */ +t_u8 num_leg_ap_chan; +int get_region_code(int *reg_code); +int process_host_cmd(int cmd, t_u8 *chan_list, t_u8 chan_num, t_u8 reg_class, + t_u8 is_intol_ap_present); +int is_intolerant_sta(int *intol); + +#endif /* _COEX_H_ */
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.c b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.c new file mode 100644 index 0000000..004e09b --- /dev/null +++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.c
@@ -0,0 +1,280 @@ +/** @file mlan2040misc.c + * + * @brief This file contains helper functions for coex application + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 06/24/2009: initial version +************************************************************************/ +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include "mlan2040coex.h" +#include "mlan2040misc.h" + +/******************************************************** + Local Variables +********************************************************/ +/** Regulatory class and Channel mapping for various regions */ +static class_chan_t us_class_chan_t[] = { + {32, {1, 2, 3, 4, 5, 6, 7}, 7}, + {33, {5, 6, 7, 8, 9, 10, 11}, 7} +}; + +static class_chan_t europe_class_chan_t[] = { + {11, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 9}, + {12, {5, 6, 7, 8, 9, 10, 11, 12, 13}, 9} +}; + +static class_chan_t japan_class_chan_t[] = { + {56, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 9}, + {57, {5, 6, 7, 8, 9, 10, 11, 12, 13}, 9}, + {58, {14}, 1} +}; + +/** Region-code(Regulatory domain) and Class-channel table mapping */ +static region_class_chan_t region_class_chan_table[] = { + {0x10, us_class_chan_t, sizeof(us_class_chan_t) / sizeof(class_chan_t)} /* US */ + , + {0x20, us_class_chan_t, sizeof(us_class_chan_t) / sizeof(class_chan_t)} /* CANADA */ + , + {0x30, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)} /* EUROPE */ + , + {0x32, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)} /* FRANCE */ + , + {0x40, japan_class_chan_t, sizeof(japan_class_chan_t) / sizeof(class_chan_t)} /* JAPAN */ + , + {0x41, japan_class_chan_t, sizeof(japan_class_chan_t) / sizeof(class_chan_t)} /* JAPAN */ + , + {0x50, europe_class_chan_t, sizeof(europe_class_chan_t) / sizeof(class_chan_t)} /* CHINA */ +}; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function prepares the channel list for a particular + * regulatory class from channel number for legacy AP + * @param cur_class_chan_table A pointer to the class_chan_t + * @param num_entry Number of entry in cur_class_chan_table table + * @param chan_list A pointer to the output channel list + * @param chan_num total number of channel in output channel list + * @param reg_domain regulatory domain + * @param reg_class regulatory class + * @param is_intol_ap_present It sets TRUE when 40MHz intolerant AP is found + * otherwise FALSE + * @return None + */ +static void +get_channels_for_specified_reg_class(class_chan_t *cur_class_chan_table, + int num_entry, t_u8 *chan_list, + t_u8 *chan_num, t_u8 reg_domain, + t_u8 reg_class, t_u8 *is_intol_ap_present) +{ + int i, j, k, idx = 0; + + *is_intol_ap_present = FALSE; + + /* For each regulatory class */ + for (i = 0; i < num_entry; i++) { + if (cur_class_chan_table[i].reg_class == reg_class) { + /* For each channel of the regulatory class */ + for (j = 0; j < cur_class_chan_table[i].total_chan; j++) { + for (k = 0; k < num_leg_ap_chan; k++) { + + if (cur_class_chan_table[i]. + channels[j] == + leg_ap_chan_list[k].chan_num) { + *(chan_list + idx) = + leg_ap_chan_list[k]. + chan_num; + idx++; + if (leg_ap_chan_list[k]. + is_intol_set) + *is_intol_ap_present = + TRUE; + } + } + } + break; + } + } + /* update the total number of channel */ + *chan_num = idx--; + return; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Prepare 2040 coex command buffer + * @param buf A pointer to the command buffer + * @param chan_list Channel list + * @param num_of_chan Number of channel present in channel list + * @param reg_class Regulatory class + * @param is_intol_ap_present Flag: is any 40 MHz intolerant AP + * is present in these chaanel set + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +void +prepare_coex_cmd_buff(t_u8 *buf, t_u8 *chan_list, t_u8 num_of_chan, + t_u8 reg_class, t_u8 is_intol_ap_present) +{ + HostCmd_DS_GEN *hostcmd; + MrvlIETypes_2040COEX_t *coex_ie = NULL; + MrvlIETypes_2040BssIntolerantChannelReport_t *bss_intol_ie = NULL; + t_u8 *pos = NULL; + int intol; + + hostcmd = (HostCmd_DS_GEN *)(buf + sizeof(t_u32)); + hostcmd->command = cpu_to_le16(HostCmd_CMD_11N_2040COEX); + hostcmd->size = S_DS_GEN; + pos = buf + sizeof(t_u32) + S_DS_GEN; + { + coex_ie = (MrvlIETypes_2040COEX_t *)pos; + coex_ie->header.element_id = TLV_ID_2040COEX; + coex_ie->header.len = sizeof(coex_ie->coex_elem); + /* Check STA is 40 MHz intolerant or not */ + is_intolerant_sta(&intol); + if (intol) + coex_ie->coex_elem |= MBIT(1); + + if (is_intol_ap_present) + coex_ie->coex_elem |= MBIT(2); + pos += sizeof(MrvlIETypes_2040COEX_t); + hostcmd->size += sizeof(MrvlIETypes_2040COEX_t); + } + { + bss_intol_ie = + (MrvlIETypes_2040BssIntolerantChannelReport_t *)pos; + bss_intol_ie->header.element_id = + TLV_ID_2040BSS_INTOL_CHAN_REPORT; + hostcmd->size += + sizeof(MrvlIETypes_2040BssIntolerantChannelReport_t) - + sizeof(bss_intol_ie->chan_num); + bss_intol_ie->reg_class = reg_class; + memcpy(bss_intol_ie->chan_num, chan_list, num_of_chan); + bss_intol_ie->header.len = + sizeof(bss_intol_ie->reg_class) + num_of_chan; + hostcmd->size += num_of_chan; + } + hostcmd->size = cpu_to_le16(hostcmd->size); + return; +} + +/** + * @brief Invoke multiple 2040Coex commands for multiple regulatory classes + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +invoke_coex_command(void) +{ + int cur_reg_domain; + t_u8 chan_list[MAX_CHAN], is_intol_ap_present; + t_u8 num_of_chan; + int i, num_entry, ret = MLAN_STATUS_SUCCESS; + class_chan_t *cur_class_chan_table = NULL; + + /** get region code */ + ret = get_region_code(&cur_reg_domain); + if (ret != MLAN_STATUS_SUCCESS) + return ret; + /** Find region_class_chan_table for this region */ + for (i = 0; + (unsigned int)i < + (sizeof(region_class_chan_table) / sizeof(region_class_chan_t)); + i++) { + if (region_class_chan_table[i].reg_domain == cur_reg_domain) { + cur_class_chan_table = + region_class_chan_table[i].class_chan_list; + num_entry = + region_class_chan_table[i].num_class_chan_entry; + break; + } + } + if (cur_class_chan_table == NULL) { + printf("No region_class_chan table found for this region\n"); + return MLAN_STATUS_FAILURE; + } + + for (i = 0; i < num_entry; i++) { + /** Get channels for the specified regulatory class */ + get_channels_for_specified_reg_class(cur_class_chan_table, + num_entry, chan_list, + &num_of_chan, + cur_reg_domain, + cur_class_chan_table[i]. + reg_class, + &is_intol_ap_present); + + /** If any channel found for this regulatory class, then invoke the 2040coex command */ + if (num_of_chan > 0) { + ret = process_host_cmd(CMD_2040COEX, chan_list, + num_of_chan, + cur_class_chan_table[i]. + reg_class, is_intol_ap_present); + if (ret) + break; + } + } + return ret; +} + +/** + * @brief Process host_cmd response + * + * @param cmd_name The command string + * @param buf A pointer to the response buffer + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_host_cmd_resp(char *cmd_name, t_u8 *buf) +{ + t_u32 hostcmd_size = 0; + HostCmd_DS_GEN *hostcmd = NULL; + int ret = MLAN_STATUS_SUCCESS; + + buf += strlen(CMD_MARVELL) + strlen(cmd_name); + memcpy((t_u8 *)&hostcmd_size, buf, sizeof(t_u32)); + buf += sizeof(t_u32); + + hostcmd = (HostCmd_DS_GEN *)buf; + hostcmd->command = le16_to_cpu(hostcmd->command); + hostcmd->size = le16_to_cpu(hostcmd->size); + + hostcmd->command &= ~HostCmd_RET_BIT; + if (!le16_to_cpu(hostcmd->result)) { + switch (hostcmd->command) { + } + } else { + printf("HOSTCMD failed: ReturnCode=%#04x, Result=%#04x\n", + le16_to_cpu(hostcmd->command), + le16_to_cpu(hostcmd->result)); + ret = MLAN_STATUS_FAILURE; + } + return ret; +}
diff --git a/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.h b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.h new file mode 100644 index 0000000..d264913 --- /dev/null +++ b/wlan_sd8897/mapp/mlan2040coex/mlan2040misc.h
@@ -0,0 +1,443 @@ +/** @file mlan2040misc.h + * + * @brief This file contains command definitions for application + * + * Copyright (C) 2009-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 06/24/2009: initial version +************************************************************************/ +#ifndef _COEX_MISC_H_ +#define _COEX_MISC_H_ + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) +/** Size of command buffer */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Command RET code, MSB is set to 1 */ +#define HostCmd_RET_BIT 0x8000 +/** General purpose action : Get */ +#define HostCmd_ACT_GEN_GET 0x0000 +/** General purpose action : Set */ +#define HostCmd_ACT_GEN_SET 0x0001 +/** TLV Id for 2040Coex IE */ +#define TLV_ID_2040COEX 0x48 +/** TLV Id for 2040BSS intolarent channel report IE */ +#define TLV_ID_2040BSS_INTOL_CHAN_REPORT 0x49 +/** Host-command for 2040coex command */ +#define HostCmd_CMD_11N_2040COEX 0x00e9 +/** Maximum scan response buffer size */ +#define SCAN_RESP_BUF_SIZE 2000 + +/** Maximum length of SSID */ +#define MRVDRV_MAX_SSID_LENGTH 32 + +/** Length of ethernet address */ +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +/** Maximum length of SSID list */ +#define MRVDRV_MAX_SSID_LIST_LENGTH 10 +/** Default scan interval in second*/ +#define DEFAULT_SCAN_INTERVAL 300 + +/** BIT value */ +#define MBIT(x) (((t_u32)1) << (x)) + +/** Check intolerent bit set */ +#define IS_INTOL_BIT_SET(cap_info) (cap_info & MBIT(14)) + +/** Check OBSS non-HT STAs present bit set */ +#define IS_NON_HT_STA_PRESENT(ht_info) (ht_info.field3 & MBIT(4)) + +/** IEEE Type definitions */ +typedef enum _IEEEtypes_ElementId_e { + SSID = 0, + SUPPORTED_RATES = 1, + FH_PARAM_SET = 2, + DS_PARAM_SET = 3, + CF_PARAM_SET = 4, + IBSS_PARAM_SET = 6, + HT_CAPABILITY = 45, + HT_OPERATION = 61, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + EXT_CAPABILITY = 127, + ERP_INFO = 42, + EXTENDED_SUPPORTED_RATES = 50, + VENDOR_SPECIFIC_221 = 221, + WMM_IE = VENDOR_SPECIFIC_221, + RSN_IE = 48, +} __attribute__ ((packed)) + IEEEtypes_ElementId_e; + +/** HT Capabilities Data */ + typedef struct _HTCap_t { + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; + /** Reserved set to 0 */ + t_u16 reserved; + } __attribute__ ((packed)) + HTCap_t, *pHTCap_t; + +/** HT Information Data */ + typedef struct _HTInfo_t { + /** Primary channel */ + t_u8 pri_chan; + /** Field 2 */ + t_u8 field2; + /** Field 3 */ + t_u16 field3; + /** Field 4 */ + t_u16 field4; + /** Bitmap indicating MCSs supported by all HT STAs in the BSS */ + t_u8 basic_mcs_set[16]; + /** Reserved set to 0 */ + t_u16 reserved; + } __attribute__ ((packed)) + HTInfo_t, *pHTInfo_t; + +/** 20/40 BSS Coexistence Data */ + typedef struct _BSSCo2040_t { + /** 20/40 BSS Coexistence value */ + t_u8 bss_co_2040_value; + /** Reserve field */ + t_u8 reserved[3]; + } __attribute__ ((packed)) + BSSCo2040_t, *pBSSCo2040_t; + +/** Extended Capabilities Data */ + typedef struct _ExtCap_t { + /** Extended Capabilities value */ + t_u8 ext_cap_value; + /** Reserved field */ + t_u8 reserved[3]; + } __attribute__ ((packed)) + ExtCap_t, *pExtCap_t; + +/** Overlapping BSS Scan Parameters Data */ + typedef struct _OverlapBSSScanParam_t { + /** OBSS Scan Passive Dwell */ + t_u16 obss_scan_passive_dwell; + /** OBSS Scan Active Dwell */ + t_u16 obss_scan_active_dwell; + /** BSS Channel Width Trigger Scan Interval */ + t_u16 bss_chan_width_trigger_scan_int; + /** OBSS Scan Passive Total Per Channel */ + t_u16 obss_scan_passive_total; + /** OBSS Scan Active Total Per Channel */ + t_u16 obss_scan_active_total; + /** BSS Width Channel Transition Delay Factor */ + t_u16 bss_width_chan_trans_delay; + /** OBSS Scan Activity Threshold */ + t_u16 obss_scan_active_threshold; + } __attribute__ ((packed)) + OBSSScanParam_t, *pOBSSScanParam_t; + +/** IEEEtypes_CapInfo_t structure*/ + typedef struct _IEEEtypes_CapInfo_t { + /** Capability Bit Map : ESS */ + t_u8 ess:1; + /** Capability Bit Map : IBSS */ + t_u8 ibss:1; + /** Capability Bit Map : CF pollable */ + t_u8 cf_pollable:1; + /** Capability Bit Map : CF poll request */ + t_u8 cf_poll_rqst:1; + /** Capability Bit Map : privacy */ + t_u8 privacy:1; + /** Capability Bit Map : Short preamble */ + t_u8 short_preamble:1; + /** Capability Bit Map : PBCC */ + t_u8 pbcc:1; + /** Capability Bit Map : Channel agility */ + t_u8 chan_agility:1; + /** Capability Bit Map : Spectrum management */ + t_u8 spectrum_mgmt:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd3:1; + /** Capability Bit Map : Short slot time */ + t_u8 short_slot_time:1; + /** Capability Bit Map : APSD */ + t_u8 apsd:1; + /** Capability Bit Map : Reserved */ + t_u8 rsvrd2:1; + /** Capability Bit Map : DSS OFDM */ + t_u8 dsss_ofdm:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd1:2; + } __attribute__ ((packed)) + IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; + + typedef struct { + t_u8 chan_number; + /**< Channel Number to scan */ + t_u8 radio_type; + /**< Radio type: 'B/G' Band = 0, 'A' Band = 1 */ + t_u8 scan_type; + /**< Scan type: Active = 1, Passive = 2 */ + t_u8 reserved; + /**< Reserved */ + t_u32 scan_time; + /**< Scan duration in milliseconds; if 0 default used */ + } __attribute__ ((packed)) + wlan_ioctl_user_scan_chan; + + typedef struct { + char ssid[MRVDRV_MAX_SSID_LENGTH + 1]; + /**< SSID */ + t_u8 max_len; /**< Maximum length of SSID */ + } __attribute__ ((packed)) + wlan_ioctl_user_scan_ssid; + + typedef struct { + + /** Flag set to keep the previous scan table intact */ + t_u8 keep_previous_scan; /* Do not erase the existing scan results */ + + /** BSS mode to be sent in the firmware command */ + t_u8 bss_mode; + + /** Configure the number of probe requests for active chan scans */ + t_u8 num_probes; + + /** Reserved */ + t_u8 reserved; + + /** BSSID filter sent in the firmware command to limit the results */ + t_u8 specific_bssid[ETH_ALEN]; + /** SSID filter list used in the to limit the scan results */ + wlan_ioctl_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + + /** Variable number (fixed maximum) of channels to scan up */ + wlan_ioctl_user_scan_chan chan_list[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; + + } __attribute__ ((packed)) + wlan_ioctl_user_scan_cfg; + +/** IEEE IE header */ + typedef struct _IEEEtypes_Header_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + } __attribute__ ((packed)) + IEEEtypes_Header_t, *pIEEEtypes_Header_t; + +/** HT Capabilities IE */ + typedef struct _IEEEtypes_HTCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTCap struct */ + HTCap_t ht_cap; + } __attribute__ ((packed)) + IEEEtypes_HTCap_t, *pIEEEtypes_HTCap_t; + +/** HT Information IE */ + typedef struct _IEEEtypes_HTInfo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTInfo struct */ + HTInfo_t ht_info; + } __attribute__ ((packed)) + IEEEtypes_HTInfo_t, *pIEEEtypes_HTInfo_t; + +/** 20/40 BSS Coexistence IE */ + typedef struct _IEEEtypes_2040BSSCo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; + } __attribute__ ((packed)) + IEEEtypes_2040BSSCo_t, *pIEEEtypes_2040BSSCo_t; + +/** Extended Capabilities IE */ + typedef struct _IEEEtypes_ExtCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** ExtCap_t struct */ + ExtCap_t ext_cap; + } __attribute__ ((packed)) + IEEEtypes_ExtCap_t, *pIEEEtypes_ExtCap_t; + +/** Overlapping BSS Scan Parameters IE */ + typedef struct _IEEEtypes_OverlapBSSScanParam_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; + } __attribute__ ((packed)) + IEEEtypes_OverlapBSSScanParam_t, *pIEEEtypes_OverlapBSSScanParam_t; + + typedef struct _wlan_get_scan_table_fixed { + /** BSSID of this network */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Channel this beacon/probe response was detected */ + t_u8 channel; + /** RSSI for the received packet */ + t_u8 rssi; + /** TSF value from the firmware at packet reception */ + t_u64 network_tsf; + } wlan_get_scan_table_fixed; + +/** + * Structure passed in the wlan_ioctl_get_scan_table_info for each + * BSS returned in the WLAN_GET_SCAN_RESP IOCTL + */ + typedef struct _wlan_ioctl_get_scan_table_entry { + /** + * Fixed field length included in the response. + * + * Length value is included so future fixed fields can be added to the + * response without breaking backwards compatibility. Use the length + * to find the offset for the bssInfoLength field, not a sizeof() calc. + */ + t_u32 fixed_field_length; + + /** + * Length of the BSS Information (probe resp or beacon) that + * follows starting at bssInfoBuffer + */ + t_u32 bss_info_length; + + /** + * Always present, fixed length data fields for the BSS + */ + wlan_get_scan_table_fixed fixed_fields; + + /* + * Probe response or beacon scanned for the BSS. + * + * Field layout: + * - TSF 8 octets + * - Beacon Interval 2 octets + * - Capability Info 2 octets + * + * - IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /* t_u8 bss_info_buffer[1]; */ + } wlan_ioctl_get_scan_table_entry; + +/** + * Sructure to retrieve the scan table + */ + typedef struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures. + * Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; + } wlan_ioctl_get_scan_table_info; + +/* Define general hostcmd data structure */ +/** HostCmd_DS_GEN */ + typedef struct _HostCmd_DS_GEN { + /** Command */ + t_u16 command; + /** Size */ + t_u16 size; + /** Sequence number */ + t_u16 seq_num; + /** Result */ + t_u16 result; + } __attribute__ ((packed)) + HostCmd_DS_GEN; + +/** Size of HostCmd_DS_GEN */ +#define S_DS_GEN sizeof(HostCmd_DS_GEN) + +/** TLV related data structures*/ +/** MrvlIEtypesHeader_t */ + typedef struct _MrvlIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; + } __attribute__ ((packed)) + MrvlIEtypesHeader_t; + +/** _MrvlIETypes_2040BssIntolerantChannelReport_t */ + typedef struct _MrvlIETypes_2040BssIntolerantChannelReport_t { + /** Header */ + IEEEtypes_Header_t header; + /** regulatory class */ + t_u8 reg_class; + /** channel numbers for legacy AP */ + t_u8 chan_num[1]; + } __attribute__ ((packed)) + MrvlIETypes_2040BssIntolerantChannelReport_t; + +/** MrvlIETypes_2040COEX_t */ + typedef struct _MrvlIETypes_2040COEX_t { + /** Header */ + IEEEtypes_Header_t header; + /** 2040 coex element */ + t_u8 coex_elem; + } __attribute__ ((packed)) + MrvlIETypes_2040COEX_t; + + typedef struct _HostCmd_DS_CMD_11N_2040COEX { + /** 2040 coex element */ + MrvlIETypes_2040COEX_t coex; + /** 2040 BSS intolerant channel report*/ + MrvlIETypes_2040BssIntolerantChannelReport_t chan_intol_report; + } __attribute__ ((packed)) + HostCmd_DS_CMD_11N_2040COEX; + +/** Maximum number of channel per regulatory class */ +#define MAX_CHAN 20 + typedef struct _class_chan_t { + /** Regulatory class */ + t_u8 reg_class; + /** Channel numbers */ + t_u8 channels[MAX_CHAN]; + /** Total number of channels */ + t_u8 total_chan; + } class_chan_t; + + typedef struct _region_class_chan_t { + /** Regulatory domain */ + int reg_domain; + /** Channel numbers */ + class_chan_t *class_chan_list; + /** Number of class channel table entry */ + int num_class_chan_entry; + } region_class_chan_t; + + int process_host_cmd_resp(char *cmd_name, t_u8 *buf); + void prepare_coex_cmd_buff(t_u8 *buf, t_u8 *chan_list, t_u8 num_of_chan, + t_u8 reg_class, t_u8 is_intol_ap_present); + int invoke_coex_command(void); + +#endif /* _COEX_MISC_H_ */
diff --git a/wlan_sd8897/mapp/mlanconfig/Makefile b/wlan_sd8897/mapp/mlanconfig/Makefile new file mode 100644 index 0000000..ba6794a --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/Makefile
@@ -0,0 +1,48 @@ +# +# File : mlanconfig/Makefile +# +# Copyright (C) 2008-2017, Marvell International Ltd. All Rights Reserved + +# Path to the top directory of the mlandriver distribution +PATH_TO_TOP = ../.. + +# Determine how we should copy things to the install directory +ABSPATH := $(filter /%, $(INSTALLDIR)) +RELPATH := $(filter-out /%, $(INSTALLDIR)) +INSTALLPATH := $(ABSPATH) +ifeq ($(strip $(INSTALLPATH)),) +INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH) +endif + +# Override CFLAGS for application sources, remove __ kernel namespace defines +CFLAGS := $(filter-out -D__%, $(ccflags-y)) +# remove KERNEL include dir +CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS)) + +# +# List of application executables to create +# +libobjs:= mlanconfig.o mlanhostcmd.o mlanmisc.o +exectarget=mlanconfig +TARGETS := $(exectarget) + +# +# Make target rules +# + +# All rule compiles list of TARGETS using builtin program target from src rule +all : +$(exectarget): $(libobjs) + $(CC) $(CFLAGS) $(libobjs) -o $(exectarget) + +# Update any needed TARGETS and then copy to the install path +build install: $(TARGETS) + @cp -rf config $(INSTALLPATH) + +clean: + @rm -f $(exectarget) + @rm -f *.o + +distclean: clean + @rm -f *~ core + @rm -f tags
diff --git a/wlan_sd8897/mapp/mlanconfig/config/11n_2040coex.conf b/wlan_sd8897/mapp/mlanconfig/config/11n_2040coex.conf new file mode 100644 index 0000000..1e8cda7 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/11n_2040coex.conf
@@ -0,0 +1,21 @@ +# File : 11n_2040coex.conf + +######################### 20/40 Coex command ################## +2040coex={ + CmdCode=0x00e9 # do NOT change this line + + 2040CoexTlvType:1=0x48 + 2040CoexTlvLen:1={ + 2040CoexElement:1=0x04 + } + + 2040BssIntlChanTlvType:1=0x49 + 2040BssIntlChanTlvLen:1={ + RegulatoryDomain:1=32 # USA: 32 (1-7), 33 (5-11) + ChannelNum:1=1 + ChannelNum:1=2 + # ... + } +} + +##################################################################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/arpfilter.conf b/wlan_sd8897/mapp/mlanconfig/config/arpfilter.conf new file mode 100644 index 0000000..a50b089 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/arpfilter.conf
@@ -0,0 +1,29 @@ +# File : arpfilter.conf + +######################### Host Sleep ARP/IP filtering command ################## +# add arp filter +# firmware supports 8 entries of ARP_FILTER. Each entry has 8 bytes. +# must not exceed 8x8+4=68 bytes + +arpfilter={ + TlvType:2=0x0115 + TlvLength:2={ + AddrType:2=3 # multicast + EthType:2=0xffff # Any + Ipv4Addr:4=0xffffffff # Any + AddrType:2=1 # broadcast + EthType:2=0x0608 # ARP: 0x0806 + Ipv4Addr:4=0x6200a8c0 # 192.168.0.98 + AddrType:2=2 # unicast + EthType:2=0xffff # Any + Ipv4Addr:4=0xffffffff # Any + } +} + +# remove arp filter +#arpfilter={ +# TlvType:2=0x0115 +# TlvLength:2={ +# } +#} +######################### Host Sleep ARP/IP filtering command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/auto_tx.conf b/wlan_sd8897/mapp/mlanconfig/config/auto_tx.conf new file mode 100644 index 0000000..6c35540 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/auto_tx.conf
@@ -0,0 +1,54 @@ +# File : auto_tx.conf + +######################### Auto-TX command ################## +auto_tx_get={ + CmdCode=0x0082 # do NOT change this line + + Action:2=0 # GET +} + +auto_tx_unreg={ + CmdCode=0x0082 # do NOT change this line + + Action:2=1 # SET +} + +nat_keep_alive={ + CmdCode=0x0082 # do NOT change this line + + Action:2=1 # SET + + AutoTxTlvType:2=0x0118 + AutoTxTlvLength:2={ # 58 = 6 + 52 (FrameLen) + Interval:2=2 # 1 - 3600 seconds + Priority:1=7 # Priority, ignored if non-WMM + Reserved:1=0 + FrameLength:2={ # 52 = 6 (DA) + 6 (SA) + 2 + 38 (Length) + DestMacAddr:6='0x00,0x40,0xf4,0xbf,0x24,0xee' + SrcMacAddr:6='0x00,0x00,0x00,0x00,0x00,0x00' + Length:2='0x00,38' # 38 = 8 (SNAP hdr) + 29 (IP) + 1 (padding) + DSAP:1=0xaa # SNAP header + SSAP:1=0xaa + Ctrl:1=0x03 + SNAP_OUI:3='0x00,0x00,0x00' + SNAP_PID:2='0x08,0x00' # IP Packet + IPv4:1=0x45 + IP_TOS:1=0x00 + IP_LEN:2='0x00,29' # IP hdr 20 + payload 9 = 29 + IP_ID:2=0xefbe + IP_Flag_FragOffset:2=0x0000 + IP_TTL:1=128 + IP_Prot:1=17 # UDP protocol + IPhdr_cksum:2=0xc5f9 # may need re-calculation if changed + IPsrcAddr:4='192,168,0,201' # 192.168.0.201 + IPdstAddr:4='192,168,0,1' # 192.168.0.1 + UDPsrcPort:2='0x11,0x94' # 4500 + UDPdstPort:2='0x11,0x94' # 4500 + UDPlength:2='0x00,9' # UDP hdr 8 + payload 1 = 9 + UDPcksum:2=0x985b # may need re-calculation if changed + UDPpayload:1=0xff + padding:1=0 # MAC Padding for 32bit alignment, set to 0 + } + } +} +######################### Auto-TX command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/bg_scan.conf b/wlan_sd8897/mapp/mlanconfig/config/bg_scan.conf new file mode 100644 index 0000000..e174936 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/bg_scan.conf
@@ -0,0 +1,157 @@ +# File : bg_scan.conf + +######################### BG Scan Configuration command ################## +########### Sample configuration for Get BG Scan Configuration ##################### +#bgscfg={ +# CmdCode=0x006b # do NOT change this line +# Action:1=0 # 0- Get, 1- Set +# ConfigType:1=0 # 0- normal BG Scan config, 1-PPS or UAPSD BG Scan config +# Enable:1=1 # 0- Disable, 1-Enable +# BssType:1=0 # 1 - Infrastructure,2 - IBSS,3 - Any +# ChannelsPerScan:1=0 # Number of Channel to scan at one scan; maximum 14 +# Reserved1:3=0 +# ScanInterval:4=0 # Interval between consecutive scan (in milliseconds) +# Reserved2:4=0 +# ReportConditions:4=0 # bit0 - SSID match + # bit1 - SNR above SNR threshold + # bit2 - RSSI above RSSI threshold + # bit31 - All channels scanned at least once +# Reserved3:2=0 +#} + +########### SET BG Scan Configuration ##################### +bgscfg={ + CmdCode=0x006b # do NOT change this line + Action:1=1 # 0- Get, 1- Set + ConfigType:1=0 # 0- normal BG Scan config, 1-PPS or UAPSD BG Scan config + Enable:1=1 # 0- Disable, 1-Enable + BssType:1=3 # 1 - Infrastructure,2 - IBSS,3 - Any + ChannelsPerScan:1=14 # Number of Channel to scan at one scan; maximum 14 + Reserved1:3=0 + ScanInterval:4=1000 # Interval between consecutive scan (in milliseconds) + Reserved2:4=0 + ReportConditions:4=1 # bit0 - SSID match + # bit1 - SNR above SNR threshold + # bit2 - RSSI above RSSI threshold + # bit31 - All channels scanned at least once + Reserved3:2=0 + + # SSID parameter set: + # + # MaxSSIDLen entries: + # + # 1. MaxSSIDLen:1=0x00 - to denote match AP name exactly, + # generate SSID specific probes + # 2. MaxSSIDLen:1=maxlen - to denote AP name will be use to base match the + # SSID and SSID's max length is 'maxlen', + # do not generate SSID specific probes + # 3. MaxSSIDLen:1=wildcard match char ('*' or '?') + # - to denote wildcard AP name will be use to match the SSID + # 4. MaxSSIDLen:1=0xff - to denote unix pattern matching + # + # SSID entries: + # + # SSID="AP_NAME" - to mention the SSID to match + + # SSID Examples: + # + # + # Match SSID name "MarvellAP" exactly, generate SSID specific probes + # + SSIDHeaderType:2=0x0112 + SSIDHeaderLen:2={ + MaxSSIDLen:1=0x00 + SSID:9="MarvellAP" + } + + # + # MarvellAP will be use to base match the SSID and SSID's max length is 12 + # +# SSIDHeaderType:2=0x0112 +# SSIDHeaderLen:2={ +# MaxSSIDLen:1=0x0c +# SSID:9="MarvellAP" +# } + + # + # Match "MarvellAP*" where '*' is a single char + # +# SSIDHeaderType:2=0x0112 +# SSIDHeaderLen:2={ +# MaxSSIDLen:1='*' +# SSID:10="MarvellAP*" +# } + + # + # Match "Mar?ell*" with unix pattern matching + # +# SSIDHeaderType:2=0x0112 +# SSIDHeaderLen:2={ +# MaxSSIDLen:1=0xff # For unix pattern matching +# SSID:8="Mar?ell*" +# } + + # Number Probe requests to be sent for broadcast and + # for each SSID specific scan required. + # + # If any SSID in the list has a non-zero modifier (wildcard match char, + # unix pattern match, maxlen), "Numprobes" of broadcast probe requests + # will be transmitted once per channel and the results matched against + # all entries. + # + # Set to 0 to use global scan probes setting + # + ProbeHeaderType:2=0x0102 + ProbeHeaderLen:2={ + NumProbes:2=2 + } + + # ChannelList contains the channels to scan + # The ChannelList should be specified in the form of + # + # RadioType, ChanNumber, ScanType, MinScanTime, ScanTime; + # + # RadioType - 0 [B/G Band], 1 [A Band] + # ScanType - 2 [Active], 3 [Passive] + # + + ChannHeaderType:2=0x0101 + ChannHeaderLen:2={ + Chan1_RadioType:1=0 + Chan1_ChanNumber:1=10 + Chan1_ScanType:1=2 + Chan1_MinScanTime:2=10 + Chan1_ScanTime:2=100 + + Chan2_RadioType:1=0 + Chan2_ChanNumber:1=6 + Chan2_ScanType:1=3 + Chan2_MinScanTime:2=10 + Chan2_ScanTime:2=100 + } + + # SNR threshold used when ReportConditions bit1 is set + SNRHeaderType:2=0x0105 + SNRHeaderLen:2={ + SNRValue:1=40 #SNR Thereshold Value + SNRFreq:1=0 + } + + # RSSI threshold used when ReportConditions bit2 is set + # + # Threshold is absolute value and match value would + # therefore be less than or equal to trigger a report + RSSIHeaderType:2=0x0104 + RSSIHeaderLen:2={ + RSSIValue:1=50 #RSSI Thereshold Value + RSSIFreq:1=0 + } + + # StartLaterValue: 0 - BGScan start immediately + # 1 - BGScan will start later after "Scan Interval" + StartLaterHeaderType:2=0x011e + StartLaterHeaderLen:2={ + StartLaterValue:2=0 + } +} +######################### BG Scan Configuration command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/bg_scan_wifidirect.conf b/wlan_sd8897/mapp/mlanconfig/config/bg_scan_wifidirect.conf new file mode 100644 index 0000000..f480a9c --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/bg_scan_wifidirect.conf
@@ -0,0 +1,88 @@ +# File : bg_scan_wifidirect.conf + +######################### BG Scan Configuration command ################## +########### SET BG Scan Configuration ##################### +bgscfg={ + CmdCode=0x006b # do NOT change this line + Action:1=1 # 0- Get, 1- Set + ConfigType:1=0 # 0- normal BG Scan config, 1-PPS or UAPSD BG Scan config + Enable:1=1 # 0- Disable, 1-Enable + BssType:1=3 # 1 - Infrastructure,2 - IBSS,3 - Any + ChannelsPerScan:1=3 # Number of Channel to scan at one scan; maximum 14 + Reserved1:3=0 + ScanInterval:4=1000 # Interval between consecutive scan (in milliseconds) + StoreCondition:4=1 # 1 - SSID match (bit 0) + # 2 - SSID match AND SNR above SNR threshold (bit 1) + ReportConditions:4=1 # 1 - SSID match (bit 0) + # 2 - SSID match AND SNR above SNR threshold (bit 1) + Reserved3:2=0 + + # SSID parameter set: + # + SSIDHeaderType:2=0x0112 + SSIDHeaderLen:2={ + MaxSSIDLen:1=0x00 + SSID:7="DIRECT-" + } + + # Number Probe requests to be sent for broadcast and + # for each SSID specific scan required. + # + # If any SSID in the list has a non-zero modifier (wildcard match char, + # unix pattern match, maxlen), "Numprobes" of broadcast probe requests + # will be transmitted once per channel and the results matched against + # all entries. + # + # Set to 0 to use global scan probes setting + # + ProbeHeaderType:2=0x0102 + ProbeHeaderLen:2={ + NumProbes:2=2 + } + + # ChannelList contains the channels to scan + # The ChannelList should be specified in the form of + # + # RadioType, ChanNumber, ScanType, MinScanTime, ScanTime; + # + # RadioType - 0 [B/G Band], 1 [A Band] + # ScanType - 2 [Active], 3 [Passive] + # + + ChannHeaderType:2=0x0101 + ChannHeaderLen:2={ + Chan1_RadioType:1=0 + Chan1_ChanNumber:1=1 + Chan1_ScanType:1=2 + Chan1_MinScanTime:2=10 + Chan1_ScanTime:2=100 + + Chan2_RadioType:1=0 + Chan2_ChanNumber:1=6 + Chan2_ScanType:1=2 + Chan2_MinScanTime:2=10 + Chan2_ScanTime:2=100 + + Chan3_RadioType:1=0 + Chan3_ChanNumber:1=11 + Chan3_ScanType:1=2 + Chan3_MinScanTime:2=10 + Chan3_ScanTime:2=100 + } + + # SNR threshold to match, when StoreCondition + # or ReportConditions been set to 2 + SNRHeaderType:2=0x0105 + SNRHeaderLen:2={ + SNRValue:1=40 #SNR Thereshold Value + SNRFreq:1=0 + } + + # StartLaterValue: 0 - BGScan start immediately + # 1 - BGScan will start later after "Scan Interval" + StartLaterHeaderType:2=0x011e + StartLaterHeaderLen:2={ + StartLaterValue:2=0 + } +} +######################### BG Scan Configuration command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/cal_data.conf b/wlan_sd8897/mapp/mlanconfig/config/cal_data.conf new file mode 100644 index 0000000..e37f674 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/cal_data.conf
@@ -0,0 +1,42 @@ +01 00 0c 00 58 02 +00 40 68 0c 00 00 00 40 00 00 00 00 00 11 00 00 +00 11 00 10 00 00 00 00 00 00 00 00 00 00 00 00 +10 12 00 10 10 86 40 89 01 03 02 00 01 02 05 00 +01 03 05 00 17 17 00 05 00 00 00 00 00 00 00 00 +00 30 1f 11 00 00 00 70 00 00 00 00 13 00 1e 01 +00 1e 5e 15 29 5e 15 13 5c 1d 0d 0b 1d 0d 0b 29 +0d 0b 29 5c 0b 29 5c 1d 00 5c 1d 0d 00 00 00 00 +00 5c c0 0e 00 00 00 cc 00 5f 00 00 07 01 04 00 +00 00 0e 0d 00 00 00 00 00 00 00 00 00 00 00 ff +00 00 00 01 00 00 00 00 00 00 00 ff 00 00 00 01 +00 00 00 00 00 00 00 ff 00 00 00 01 00 00 00 00 +00 00 00 ff 00 00 00 01 00 00 00 00 06 3c 06 3d +00 00 00 00 00 00 00 00 00 00 00 00 +00 5c dc 25 00 00 01 28 00 6f 00 00 07 01 04 00 +00 00 0e 0d 00 00 00 00 00 00 00 00 00 08 00 07 +00 00 00 09 00 00 00 00 00 08 00 07 00 00 00 09 +00 00 00 00 00 08 00 07 00 00 00 09 00 00 00 00 +00 08 00 07 00 00 00 09 00 00 00 00 06 3c 06 3d +00 00 00 00 00 00 00 00 00 00 00 00 +00 14 9f 1f 00 00 01 3c 03 00 00 00 00 f1 0a f1 +00 fb 0d fb +00 20 dd 28 00 00 01 5c 08 86 00 88 ff 06 b1 05 +24 24 3c 42 00 00 24 18 a4 24 bc bc 3d 00 a0 8f +00 14 00 2a 00 00 01 70 00 00 30 00 01 05 1b 00 +00 00 00 01 +00 74 2c 10 00 00 01 e4 00 00 00 00 09 6a 09 b0 +0b 12 00 6c 04 0a 00 6c 03 03 00 6c 03 03 00 6c +3f ff ff 00 3f ff ff 01 3f ff ff 02 3f ff ff 03 +15 00 00 04 17 00 00 05 19 00 00 06 1b 00 00 07 +1d 00 00 08 1f 00 00 09 21 00 00 0a 23 00 00 0b +25 00 00 0c 28 00 00 0d 2a 00 00 0e 2d 00 00 0f +2f 00 00 10 32 00 00 11 34 00 00 12 3f ff ff 13 +3f ff ff 14 +00 74 84 10 ff ff ff ff 01 00 00 00 09 b0 09 ba +0a 0f 00 6c 04 09 00 6c 03 03 00 6c 03 03 00 6c +3f ff ff 00 3f ff ff 01 3f ff ff 02 3f ff ff 03 +15 00 00 04 17 00 00 05 1a 00 00 06 1c 00 00 07 +1f 00 00 08 21 00 00 09 23 00 00 0a 26 00 00 0b +2a 00 00 0c 2d 00 00 0d 31 00 00 0e 34 00 00 0f +3f ff ff 10 3f ff ff 11 3f ff ff 12 3f ff ff 13 +3f ff ff 14
diff --git a/wlan_sd8897/mapp/mlanconfig/config/crypto_test.conf b/wlan_sd8897/mapp/mlanconfig/config/crypto_test.conf new file mode 100644 index 0000000..9b911d2 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/crypto_test.conf
@@ -0,0 +1,57 @@ +# File : crypto_test.conf + +######################### crypto_test command configuration ################## + +crypto_test={ + CmdCode=0x0078 # do NOT change this line + #EncDec: 0-Decrypt, 1-Encrypt + EncDec:2=0 + #Algorithm: 1-RC4, 2-AES, 3-AES_KEY_WRAP + Algorithm:2=1 + #KeyIVLength: Length of KeyIV (bytes) + KeyIVLength:2=8 + #KeyIV: Key IV + KeyIV:32='0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11' + #KeyLength: Length of Key (bytes) + KeyLength:2=16 + #Key: Key + Key:32='0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22' + #DataType: DataType + DataType:2=0x0111 + #DataLength: Data Length + DataLength:2={ + #Data: Data + Data:8='0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33' + } +} + +#####Sample crypto_test command configuration for AES-CCM algorithm ######### + +#crypto_test={ +# CmdCode=0x0078 # do NOT change this line +# #EncDec: 0-Decrypt, 1-Encrypt +# EncDec:2=1 +# #Algorithm: 4-AES-CCM +# Algorithm:2=4 +# #KeyLength: Length of Key (bytes) +# KeyLength:2=16 +# #Key: Key +# Key:32='0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22' +# #NonceLength: Length of Nonce (bytes) +# NonceLength:2=10 +# #Nonce: Nonce +# Nonce:14='0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11' +# #AADLength: Length of AAD (bytes) +# AADLength:2=12 +# #AAD: AAD +# AAD:32='0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33' +# #DataType: DataType +# DataType:2=0x0111 +# #DataLength: Data Length +# DataLength:2={ +# #Data: Data +# Data:8='0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33' +# } +#} + +######################### End of crypto_test configuration command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/cwmode.conf b/wlan_sd8897/mapp/mlanconfig/config/cwmode.conf new file mode 100644 index 0000000..1f245a2 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/cwmode.conf
@@ -0,0 +1,51 @@ +#CW_MODE settings configuration +# +CW_MODE={ +#Mode of operation 0: Disable current Tx Mode, 1: Continuous Tx Packet, 2: Continuous Wave/Tone +Mode=1 +#Channel number +Channel=6 +#channel Info +#bit [3:0] - channel offset 0: no secondary channel, 1: secondary channel above, 2: reserved, 3: secondary channel below +#bit [4:7] - channel band 0: 2.4G, 1: 5G, 2: 4G, 3: reserved +Chaninfo=0x00 +#Tx power for Cont Tx in dbm +TxPower=12 +# Packet Length for Cont Tx +# legacy rates: upto 2312, HT/VHT rates: upto 8K +PktLength=2048 +#RateInfo for Cont Tx +RateInfo=0x0000 +#rateInfo = 0x0732 : format = VHT, Bandwidth = 80MHz, datarate/MCS index = MCS7 +#rateInfo = 0x0000 : format = legacy, Bandwidth = 20MHz, datarate/MCS index = 1Mbps +#rateInfo = 0x0521 : format = HT, Bandwidth = 40MHz, datarate/MCS index = MCS5 +#rateInfo = 0x4521 : format = HT, Bandwidth = 40MHz, datarate/MCS index = MCS5, NSS = 1 +#rateInfo = 0x8921 : format = HT, Bandwidth = 40MHz, datarate/MCS index = MCS9, NSS = 2 +#bit [1:0] - Format 0: legacy, 1: 11n (HT), 2: 11ac (VHT) +#bit 2 - STBC (Not used for Cont Tx test) +#bit 3 - Beamforming (Not used for Cont Tx test) +#bit [5:4] - Bandwidth 0: 20MHz, 2: 40MHz, 3: 80MHz +#bit [7:6] - reserved +#bit [13:8] - index : data rate or MCS +#bit [15:14]- NSS (Used for 2x2 chips). Used in FW only for 2x2 chips. +#<index> can take following values. +#If <format> is 0 (LG), #If <format> is 1 (HT) #If <format> is 2 (VHT) +# NSS = 1 NSS = 1 or 2 +# 0 1 Mbps 0 MCS0 0 MCS0 +# 1 2 Mbps 1 MCS1 1 MCS1 +# 2 5.5 Mbps 2 MCS2 2 MCS2 +# 3 11 Mbps 3 MCS3 3 MCS3 +# 5 6 Mbps 4 MCS4 4 MCS4 +# 6 9 Mbps 5 MCS5 5 MCS5 +# 7 12 Mbps 6 MCS6 6 MCS6 +# 8 18 Mbps 7 MCS7 7 MCS7 +# Used only for NSS = 2 +# 9 24 Mbps 8 MCS8 +# 10 36 Mbps 9 MCS9 +# 11 48 Mbps 10 MCS10 +# 12 54 Mbps 11 MCS11 +# 12 MCS12 +# 13 MCS13 +# 14 MCS14 +# 15 MCS15 +}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/ed_mac_ctrl.conf b/wlan_sd8897/mapp/mlanconfig/config/ed_mac_ctrl.conf new file mode 100644 index 0000000..ae0f750 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/ed_mac_ctrl.conf
@@ -0,0 +1,74 @@ +# File : ed_mac_ctrl.conf +# +# ./mlanutl mlan0 hostcmd config/ed_mac_ctrl.conf ed_mac_ctrl +# +# ed_mac_ctrl_v2 should be used for KF/CAC +# ./mlanutl mlan0 hostcmd config/ed_mac_ctrl.conf ed_mac_ctrl_v2 +# +# ed_mac_ctrl_v3 should be used for KF2/RB3/CA2 +# ./mlanutl mlan0 hostcmd config/ed_mac_ctrl.conf ed_mac_ctrl_v3 +# +## Set ED MAC control +ed_mac_ctrl={ + CmdCode=0x0124 # do NOT change this line + Enable:2=0x0 # 0 - disable, use default + # 1 - enable + + Offset:2=0x0 # 0 - no offset + # 0xffff - offset set to -1 + # 0x1 - offset set to 1 +} + +ed_mac_ctrl_v2={ + CmdCode=0x0130 #Command code, do NOT change this tile + ed_ctrl_2g.enable:2=0x00 #0 - disable, all other parameters are ignored in 2.4G band + #1 - enable + #0xff - use default OR value + ed_ctrl_2g.offset:2=0x0 #offset - value rabge: -128~127 + ed_ctrl_2g.on_period:2=0x12 #time in millisecond to keep ED control enabled in 2.4g + ed_ctrl_2g.off_period:2=0x0 #time in milliseconf to keep ED control disabled in 2.4g + ed_ctrl_2g.bitmap:2=0x1 #Factors that affect 2.4G band ED control behavior + #Bit0: 0=If IEEE power save mode is on, disable EDCtrl2g + # 1=Neglect IEEE power save mode + #Bit8: 0=If IEEE power save mode is on EDCtrl2g is 1, + # always keep EDCtrl2g enabled as if EDOffPeriod2g is 0 + # 1=Neglect IEEE power save mode + + ed_ctrl_5g.enable:2=0x0 #0 - disable, all other parameters are ignored in 2.4G band + #1 - enable + #0xff - use default OR value + ed_ctrl_5g.offset:2=0x0 #offset - value rabge: -128~127 + ed_ctrl_5g.on_period:2=0x12 #time in millisecond to keep ED control enabled in 5g + ed_ctrl_5g.off_period:2=0x0 #time in milliseconf to keep ED control disabled in 5g + ed_ctrl_5g.bitmap:2=0x1 #Factors that affect 5G band ED control behavior + #Bit0: 0=If IEEE power save mode is on, disable EDCtrl5g + # 1=Neglect IEEE power save mode + #Bit8: 0=If IEEE power save mode is on EDCtrl5g is 1, + # always keep EDCtrl2g enabled as if EDOffPeriod5g is 0 + # 1=Neglect IEEE power save mode +} + +ed_mac_ctrl_v3={ + CmdCode=0x0130 #Command code, do NOT change this tile + ed_ctrl_2g.enable:2=0x01 #0 - disable EDMAC feature in 2.4G band + #1 - enable + ed_ctrl_2g.offset:2=0x0 #offset - value range: -128~127 + #this offset will be added to the EDMAC thresholds + #effective_thr = default_thr + offset + ed_ctrl_5g.enable:2=0x01 #0 - disable EDMAC feature in 5G band + #1 - enable + ed_ctrl_5g.offset:2=0x0 #offset - value range: -128~127 + #this offset will be added to the EDMAC thresholds + #effective_thr = default_thr + offset + ed_ctrl_txq_lock:4=0xFF #Bitmap to control which queues to lock when ED busy is high + #The bitmap controls the following Tx when ED is high: + #Bit23 - PS queue + #Bit22 - CF queue + #Bit21 - MC queue + #Bit20 - TS queue + #Bit10 - Beacon TBTT + #Bit9 - BWQ + #Bit8 - CMQ + #Bit7:0 - TCQ7:0 + #Respective Tx is blocked when the bit is set. +}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/host_tdls.conf b/wlan_sd8897/mapp/mlanconfig/config/host_tdls.conf new file mode 100644 index 0000000..3316d79 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/host_tdls.conf
@@ -0,0 +1,22 @@ +# File : host_tdls.conf + +######################### HOST TDLS commands configuration ################## +# starts with a command name with parameters embedded inside + +host_tdls_config={ + uapsd_support=1 # 1: suport uapsd, 0: uapsd not support + cs_support=1 # 1: support channel switch, 0: don't support channel switch + SupportedChannels={ + FirstChannelNo=1 + NumberofSubBandChannels=11 + # multiple instances of First Channel No, Number of SubBand Channels + } + SupportedRegulatoryClasses={ + CurrentRegulatoryClass=1 + NumofRegulatoryClasses=15 + ListOfRegulatoryClasses=1,2,3,4,12,22,23,24,25,27,28,29,30,32,33 + } + +} + +######################### HOST TDLS commands configuration ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/init_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/init_cfg.conf new file mode 100644 index 0000000..d37cc4d --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/init_cfg.conf
@@ -0,0 +1,11 @@ +# File : init_cfg.conf + +# MAC address (interface: address) +mac_addr=mlan0: 00:50:43:20:12:34 +mac_addr=uap0: 00:50:43:20:12:35 +mac_addr=wfd0: 00:50:43:20:12:36 + +# Register (type, offset, value) +# type 1:MAC/SOC, 2:BBP, 3:RF, 5:CAU +wlan_reg=1,0xA794,0x55FF55FF +
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mef.conf b/wlan_sd8897/mapp/mlanconfig/config/mef.conf new file mode 100644 index 0000000..37d19a4 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/mef.conf
@@ -0,0 +1,167 @@ +# File : mef.conf + +######################### MEF Configuration command ################## +mefcfg={ + #Criteria: bit0-broadcast, bit1-unicast, bit3-multicast + Criteria=2 # Unicast frames are received during hostsleepmode + NumEntries=1 # Number of activated MEF entries + #mef_entry_0: example filters to match TCP destination port 80 send by 192.168.0.88 pkt or magic pkt. + mef_entry_0={ + #mode: bit0--hostsleep mode, bit1--non hostsleep mode + mode=1 # HostSleep mode + #action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host + action=3 # Allow and Wake host + filter_num=3 # Number of filter + #RPN only support "&&" and "||" operator,space can not be removed between operator. + RPN=Filter_0 && Filter_1 || Filter_2 + #Byte comparison filter's type is 0x41,Decimal comparison filter's type is 0x42, + #Bit comparison filter's type is 0x43 + #Filter_0 is decimal comparison filter, it always with type=0x42 + #Decimal filter always has type, pattern, offset, numbyte 4 field + #Filter_0 will match rx pkt with TCP destination port 80 + Filter_0={ + type=0x42 # decimal comparison filter + pattern=80 # 80 is the decimal constant to be compared + offset=44 # 44 is the byte offset of the field in RX pkt to be compare + numbyte=2 # 2 is the number of bytes of the field + } + #Filter_1 is Byte comparison filter, it always with type=0x41 + #Byte filter always has type, byte, repeat, offset 4 filed + #Filter_1 will match rx pkt send by IP address 192.168.0.88 + Filter_1={ + type=0x41 # Byte comparison filter + repeat=1 # 1 copies of 'c0:a8:00:58' + byte=c0:a8:00:58 # 'c0:a8:00:58' is the byte sequence constant with each byte + # in hex format, with ':' as delimiter between two byte. + offset=34 # 34 is the byte offset of the equal length field of rx'd pkt. + } + #Filter_2 is Magic packet, it will looking for 16 contiguous copies of '00:50:43:20:01:02' from + # the rx pkt's offset 14 + Filter_2={ + type=0x41 # Byte comparison filter + repeat=16 # 16 copies of '00:50:43:20:01:02' + byte=00:50:43:20:01:02 # '00:50:43:20:01:02' is the byte sequence constant + offset=14 # 14 is the byte offset of the equal length field of rx'd pkt. + } + } +} + + +#--------------------------examples for MEF filters-------------------------------- +# example: filters to match ARP packet with protocol addr 192.168.0.104 +# mef_entry_0={ +# mode=1 # HostSleep mode +# action=3 # Allow and Wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 && Filter_1 && Filter_2 +# #Filter_0 looking for rx pkt with DA is broadcast address +# Filter_0={ +# type=0x41 +# repeat=6 +# byte=ff +# offset=0 +# } +# #Filter_1 looking for rx pkt with EtherType is 0x0806(ARP) +# Filter_1={ +# type=0x41 +# repeat=1 +# byte=08:06 +# offset=20 +# } +# #Filter_2 looking for rx pkt with ARP target protocol addr 192.168.0.104 +# Filter_2={ +# type=0x41 +# repeat=1 +# byte=c0:a8:00:68 +# offset=46 +# } +# } +#------------------------------------------------------------------------------------- +# example: filter to check if the destination MAC address is unicast pkt +# mef_entry_0={ +# mode=1 # HostSleep mode +# action=3 # Allow and Wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 +# #Filter_0 is Bit comparison filter, it always with type=0x43 +# #Byte filter always has type, byte, mask, offset 4 filed +# #"byte" is the byte sequence constant with each byte in hex format, with ':' as delimiter between two byte +# #"mask" is also with each byte in hex format, with ':' as delimiter between two byte +# #"byte" should has the same length as "mask" +# #Filter_0 will check if the destination MAC address is unicast pkt +# Filter_0={ +# type=0x43 #Bit comparison filter +# byte=00 #00 is the 1-byte sequence constant +# offset=0 #0 is the byte offset of the rx pkt +# mask=01 #1 is the 1-byte mask +# } +# } +#-------------------------------------------------------------------------------------------------- +# example: Disable MEF filters +# mefcfg={ +# #Criteria: bit0-broadcast, bit1-unicast, bit3-multicast +# Criteria=2 # Unicast frames are received during hostsleepmode +# NumEntries=0 # Number of activated MEF entries +# } +#-------------------------------------------------------------------------------------------------- +# example: Test MEF filters +# mefcfg={ +# Criteria=1 +# NumEntries=1 +# mef_entry_0={ +# mode=4 # Test Mode +# action=16 # Invoke Test +# filter_num=0 +# } +# } +#----------------------------------------------------------------------------------------------------- +# example: Test MEF filters +# mefcfg={ +# Criteria=1 +# NumEntries=1 +# mef_entry_0={ +# mode=4 +# action=0 +# filter_num=1 +# RPN=Filter_0 +# Filter_0={ +# type=0x44 # test filter +# repeat=2 # 2 copies of 'BE:EF' +# byte=BE:EF # 'BE:EF' is the byte sequence constant +# offset=18 # 18 is the byte offset of the equal length field of rx'd pkt. +# dest=00:50:43:20:5a:82 # '00:50:43:20:5a:82' is the byte sequence constant +# } +# } +# } +#---------------------------------------------------------------------------------------------------- +#example: Filter broadcast/ipv4 multicast/ipv6 multicast packets in non hostsleep mode +#mefcfg={ +# Criteria=9 # broadcast and multicast frames +# NumEntries=1 # Number of activated MEF entries +# mef_entry_0={ +# mode=2 # non HostSleep mode +# action=0 # discard and not wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 || Filter_1 || Filter_2 +# Filter_0={ # IPV4 multicast +# type=0x41 # byte comparison filter +# byte=01:00:5e # 01:00:5e is the byte constant to be compared +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# repeat=1 # +# } +# Filter_1={ # broadcast packet check +# type=0x41 # Byte comparison filter +# repeat=6 # 6 copies of 'ff', means broadcast +# byte=ff # 'ff'(0xff) is the byte sequence constant with each byte +# # in hex format, with ':' as delimiter between two byte. +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# } +# Filter_2={ # IPV6 multicast +# type=0x41 # byte comparison filter +# byte=33:33 # 33:33 is the byte constant to be compared +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# repeat=1 # +# } +# } +#} +#------------------------------------------------------------------------------------------------------
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mef_mdns.conf b/wlan_sd8897/mapp/mlanconfig/config/mef_mdns.conf new file mode 100644 index 0000000..568ca6f --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/mef_mdns.conf
@@ -0,0 +1,204 @@ +# File : mef_mdns.conf + +######################### MEF Configuration command ################## +mefcfg={ + #Criteria: bit0-broadcast, bit1-unicast, bit3-multicast + Criteria=8 # Multicast frames are received during hostsleepmode + NumEntries=2 # Number of activated MEF entries + #mef_entry_0: example filters to match WS-Discovery pkt for IPv4. + mef_entry_0={ + #mode: bit0--hostsleep mode, bit1--non hostsleep mode + mode=1 # HostSleep mode + #action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host + action=3 # Allow and Wake host + filter_num=4 # Number of filter + #RPN only support "&&" and "||" operator,space can not be removed between operator. + RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3 + #Filter_0 will match IPv4 protocol packet + Filter_0={ + type=0x41 + repeat=1 + byte=08:00 + offset=20 + } + #Filter_1 will match dest multicast IPv4 address 224.0.0.251 + Filter_1={ + type=0x41 + repeat=1 + byte=e0:00:00:fb + offset=38 + } + #Filter_2 will match UDP packet + Filter_2={ + type=0x42 + pattern=17 + offset=31 + numbyte=1 + } + #Filter_3 will match UDP port 5353 + Filter_3={ + type=0x42 + pattern=5353 + offset=44 + numbyte=2 + } + } + #mef_entry_1: example filters to match WS-Discovery pkt for IPv6. + mef_entry_1={ + #mode: bit0--hostsleep mode, bit1--non hostsleep mode + mode=1 # HostSleep mode + #action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host + action=3 # Allow and Wake host + filter_num=4 # Number of filter + #RPN only support "&&" and "||" operator,space can not be removed between operator. + RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3 + #Filter_0 will match IPv4 protocol packet + Filter_0={ + type=0x41 + repeat=1 + byte=86:dd + offset=20 + } + #Filter_1 will match dest multicast IPv6 address FF02::FB + Filter_1={ + type=0x41 + repeat=1 + byte=ff:02:00:00:00:00:00:00:00:00:00:00:00:00:00:fb + offset=46 + } + #Filter_2 will match UDP packet + Filter_2={ + type=0x42 + pattern=17 + offset=28 + numbyte=1 + } + #Filter_3 will match UDP port 5353 + Filter_3={ + type=0x42 + pattern=5353 + offset=64 + numbyte=2 + } + } +} + + +#--------------------------examples for MEF filters-------------------------------- +# example: filters to match ARP packet with protocol addr 192.168.0.104 +# mef_entry_0={ +# mode=1 # HostSleep mode +# action=3 # Allow and Wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 && Filter_1 && Filter_2 +# #Filter_0 looking for rx pkt with DA is broadcast address +# Filter_0={ +# type=0x41 +# repeat=6 +# byte=ff +# offset=0 +# } +# #Filter_1 looking for rx pkt with EtherType is 0x0806(ARP) +# Filter_1={ +# type=0x41 +# repeat=1 +# byte=08:06 +# offset=20 +# } +# #Filter_2 looking for rx pkt with ARP target protocol addr 192.168.0.104 +# Filter_2={ +# type=0x41 +# repeat=1 +# byte=c0:a8:00:68 +# offset=46 +# } +# } +#------------------------------------------------------------------------------------- +# example: filter to check if the destination MAC address is unicast pkt +# mef_entry_0={ +# mode=1 # HostSleep mode +# action=3 # Allow and Wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 +# #Filter_0 is Bit comparison filter, it always with type=0x43 +# #Byte filter always has type, byte, mask, offset 4 filed +# #"byte" is the byte sequence constant with each byte in hex format, with ':' as delimiter between two byte +# #"mask" is also with each byte in hex format, with ':' as delimiter between two byte +# #"byte" should has the same length as "mask" +# #Filter_0 will check if the destination MAC address is unicast pkt +# Filter_0={ +# type=0x43 #Bit comparison filter +# byte=00 #00 is the 1-byte sequence constant +# offset=0 #0 is the byte offset of the rx pkt +# mask=01 #1 is the 1-byte mask +# } +# } +#-------------------------------------------------------------------------------------------------- +# example: Disable MEF filters +# mefcfg={ +# #Criteria: bit0-broadcast, bit1-unicast, bit3-multicast +# Criteria=2 # Unicast frames are received during hostsleepmode +# NumEntries=0 # Number of activated MEF entries +# } +#-------------------------------------------------------------------------------------------------- +# example: Test MEF filters +# mefcfg={ +# Criteria=1 +# NumEntries=1 +# mef_entry_0={ +# mode=4 # Test Mode +# action=16 # Invoke Test +# filter_num=0 +# } +# } +#----------------------------------------------------------------------------------------------------- +# example: Test MEF filters +# mefcfg={ +# Criteria=1 +# NumEntries=1 +# mef_entry_0={ +# mode=4 +# action=0 +# filter_num=1 +# RPN=Filter_0 +# Filter_0={ +# type=0x44 # test filter +# repeat=2 # 2 copies of 'BE:EF' +# byte=BE:EF # 'BE:EF' is the byte sequence constant +# offset=18 # 18 is the byte offset of the equal length field of rx'd pkt. +# dest=00:50:43:20:5a:82 # '00:50:43:20:5a:82' is the byte sequence constant +# } +# } +# } +#---------------------------------------------------------------------------------------------------- +#example: Filter broadcast/ipv4 multicast/ipv6 multicast packets in non hostsleep mode +#mefcfg={ +# Criteria=9 # broadcast and multicast frames +# NumEntries=1 # Number of activated MEF entries +# mef_entry_0={ +# mode=2 # non HostSleep mode +# action=0 # discard and not wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 || Filter_1 || Filter_2 +# Filter_0={ # IPV4 multicast +# type=0x41 # byte comparison filter +# byte=01:00:5e # 01:00:5e is the byte constant to be compared +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# repeat=1 # +# } +# Filter_1={ # broadcast packet check +# type=0x41 # Byte comparison filter +# repeat=6 # 6 copies of 'ff', means broadcast +# byte=ff # 'ff'(0xff) is the byte sequence constant with each byte +# # in hex format, with ':' as delimiter between two byte. +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# } +# Filter_2={ # IPV6 multicast +# type=0x41 # byte comparison filter +# byte=33:33 # 33:33 is the byte constant to be compared +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# repeat=1 # +# } +# } +#} +#------------------------------------------------------------------------------------------------------
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mef_ws_discovery.conf b/wlan_sd8897/mapp/mlanconfig/config/mef_ws_discovery.conf new file mode 100644 index 0000000..518a240 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/mef_ws_discovery.conf
@@ -0,0 +1,204 @@ +# File : mef_ws_discovery.conf + +######################### MEF Configuration command ################## +mefcfg={ + #Criteria: bit0-broadcast, bit1-unicast, bit3-multicast + Criteria=8 # Multicast frames are received during hostsleepmode + NumEntries=2 # Number of activated MEF entries + #mef_entry_0: example filters to match WS-Discovery pkt for IPv4. + mef_entry_0={ + #mode: bit0--hostsleep mode, bit1--non hostsleep mode + mode=1 # HostSleep mode + #action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host + action=3 # Allow and Wake host + filter_num=4 # Number of filter + #RPN only support "&&" and "||" operator,space can not be removed between operator. + RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3 + #Filter_0 will match IPv4 protocol packet + Filter_0={ + type=0x41 + repeat=1 + byte=08:00 + offset=20 + } + #Filter_1 will match dest multicast IPv4 address 239.255.255.250 + Filter_1={ + type=0x41 + repeat=1 + byte=ef:ff:ff:fa + offset=38 + } + #Filter_2 will match UDP packet + Filter_2={ + type=0x42 + pattern=17 + offset=31 + numbyte=1 + } + #Filter_3 will match UDP port 3702 + Filter_3={ + type=0x42 + pattern=3702 + offset=44 + numbyte=2 + } + } + #mef_entry_1: example filters to match WS-Discovery pkt for IPv6. + mef_entry_1={ + #mode: bit0--hostsleep mode, bit1--non hostsleep mode + mode=1 # HostSleep mode + #action: 0--discard and not wake host, 1--discard and wake host 3--allow and wake host + action=3 # Allow and Wake host + filter_num=4 # Number of filter + #RPN only support "&&" and "||" operator,space can not be removed between operator. + RPN=Filter_0 && Filter_1 && Filter_2 && Filter_3 + #Filter_0 will match IPv4 protocol packet + Filter_0={ + type=0x41 + repeat=1 + byte=86:dd + offset=20 + } + #Filter_1 will match dest multicast IPv6 address FF02::C + Filter_1={ + type=0x41 + repeat=1 + byte=ff:02:00:00:00:00:00:00:00:00:00:00:00:00:00:0c + offset=46 + } + #Filter_2 will match UDP packet + Filter_2={ + type=0x42 + pattern=17 + offset=28 + numbyte=1 + } + #Filter_3 will match UDP port 3702 + Filter_3={ + type=0x42 + pattern=3702 + offset=64 + numbyte=2 + } + } +} + + +#--------------------------examples for MEF filters-------------------------------- +# example: filters to match ARP packet with protocol addr 192.168.0.104 +# mef_entry_0={ +# mode=1 # HostSleep mode +# action=3 # Allow and Wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 && Filter_1 && Filter_2 +# #Filter_0 looking for rx pkt with DA is broadcast address +# Filter_0={ +# type=0x41 +# repeat=6 +# byte=ff +# offset=0 +# } +# #Filter_1 looking for rx pkt with EtherType is 0x0806(ARP) +# Filter_1={ +# type=0x41 +# repeat=1 +# byte=08:06 +# offset=20 +# } +# #Filter_2 looking for rx pkt with ARP target protocol addr 192.168.0.104 +# Filter_2={ +# type=0x41 +# repeat=1 +# byte=c0:a8:00:68 +# offset=46 +# } +# } +#------------------------------------------------------------------------------------- +# example: filter to check if the destination MAC address is unicast pkt +# mef_entry_0={ +# mode=1 # HostSleep mode +# action=3 # Allow and Wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 +# #Filter_0 is Bit comparison filter, it always with type=0x43 +# #Byte filter always has type, byte, mask, offset 4 filed +# #"byte" is the byte sequence constant with each byte in hex format, with ':' as delimiter between two byte +# #"mask" is also with each byte in hex format, with ':' as delimiter between two byte +# #"byte" should has the same length as "mask" +# #Filter_0 will check if the destination MAC address is unicast pkt +# Filter_0={ +# type=0x43 #Bit comparison filter +# byte=00 #00 is the 1-byte sequence constant +# offset=0 #0 is the byte offset of the rx pkt +# mask=01 #1 is the 1-byte mask +# } +# } +#-------------------------------------------------------------------------------------------------- +# example: Disable MEF filters +# mefcfg={ +# #Criteria: bit0-broadcast, bit1-unicast, bit3-multicast +# Criteria=2 # Unicast frames are received during hostsleepmode +# NumEntries=0 # Number of activated MEF entries +# } +#-------------------------------------------------------------------------------------------------- +# example: Test MEF filters +# mefcfg={ +# Criteria=1 +# NumEntries=1 +# mef_entry_0={ +# mode=4 # Test Mode +# action=16 # Invoke Test +# filter_num=0 +# } +# } +#----------------------------------------------------------------------------------------------------- +# example: Test MEF filters +# mefcfg={ +# Criteria=1 +# NumEntries=1 +# mef_entry_0={ +# mode=4 +# action=0 +# filter_num=1 +# RPN=Filter_0 +# Filter_0={ +# type=0x44 # test filter +# repeat=2 # 2 copies of 'BE:EF' +# byte=BE:EF # 'BE:EF' is the byte sequence constant +# offset=18 # 18 is the byte offset of the equal length field of rx'd pkt. +# dest=00:50:43:20:5a:82 # '00:50:43:20:5a:82' is the byte sequence constant +# } +# } +# } +#---------------------------------------------------------------------------------------------------- +#example: Filter broadcast/ipv4 multicast/ipv6 multicast packets in non hostsleep mode +#mefcfg={ +# Criteria=9 # broadcast and multicast frames +# NumEntries=1 # Number of activated MEF entries +# mef_entry_0={ +# mode=2 # non HostSleep mode +# action=0 # discard and not wake host +# filter_num=3 # Number of filter +# RPN=Filter_0 || Filter_1 || Filter_2 +# Filter_0={ # IPV4 multicast +# type=0x41 # byte comparison filter +# byte=01:00:5e # 01:00:5e is the byte constant to be compared +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# repeat=1 # +# } +# Filter_1={ # broadcast packet check +# type=0x41 # Byte comparison filter +# repeat=6 # 6 copies of 'ff', means broadcast +# byte=ff # 'ff'(0xff) is the byte sequence constant with each byte +# # in hex format, with ':' as delimiter between two byte. +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# } +# Filter_2={ # IPV6 multicast +# type=0x41 # byte comparison filter +# byte=33:33 # 33:33 is the byte constant to be compared +# offset=0 # 0 is the byte offset of the equal length field of rx'd pkt. +# repeat=1 # +# } +# } +#} +#------------------------------------------------------------------------------------------------------
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mem.conf b/wlan_sd8897/mapp/mlanconfig/config/mem.conf new file mode 100644 index 0000000..cf649c9 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/mem.conf
@@ -0,0 +1,17 @@ +#File : memory/register update +# memory/register update file +mem_set={ + CmdCode=0x0086 #do NOT change this line + Action:2=1 # 1 - SET + Rsrvd:2=0 + Addr:4=0x8000a584 # memory/register address + Value:4=0x16161669 #Value +} + +mem_get={ + CmdCode=0x0086 #do NOT change this line + Action:2=0 # 1 - SET + Rsrvd:2=0 + Addr:4=0x8000a584 # memory/register address + Result:4=0 +}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mgmt_frame.conf b/wlan_sd8897/mapp/mlanconfig/config/mgmt_frame.conf new file mode 100644 index 0000000..22b19b1 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/mgmt_frame.conf
@@ -0,0 +1,30 @@ +# Format of the packet data to be sent. +# Update the TxControl value for TxPD, else zero is sent. +# all packet data is written under "Data" block + +PktType=0x00 # should be zero for MGMT frames +PktSubType=0x05 + # Assoc Request 0 + # Assoc Response 1 + # Re-Assoc Request 2 + # Re-Assoc Response 3 + # Probe Request 4 + # Probe Response 5 + # Beacon 8 + # Atim 9 + # Dis-assoc 10 + # Auth 11 + # Deauth 12 + # Action Frame 13 + +FromDS=0 +ToDS=0 + +Addr1=00:50:43:27:B0:41 # Destination address +Addr2=00:50:43:21:0F:84 # Source address +Addr3=00:50:43:21:0F:84 # BSSID + +Data=0x01,0x01,0x00,0x0c,0x00,0x58,0x02,0x40 + +SeqNum=0 +FragNum=0
diff --git a/wlan_sd8897/mapp/mlanconfig/config/mgmtfilter.conf b/wlan_sd8897/mapp/mlanconfig/config/mgmtfilter.conf new file mode 100644 index 0000000..8ee0298 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/mgmtfilter.conf
@@ -0,0 +1,15 @@ +##############management frame filter to wake up host ########### +##### support two entries currently ###### +mgmtfilter={ + entry_num=1 + entry_0={ + action=1 # discard and wakeup host + type=0x1 # p2p frames + frame_mask=0x7 # Go neg req & rsp & cfm frame + } +# entry_1={ +# action=0 # discard and not wakeup host +# type=0xff # management frames +# frame_mask=0x3 # assoc req & rsp frame +# } +}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/or_data.conf b/wlan_sd8897/mapp/mlanconfig/config/or_data.conf new file mode 100644 index 0000000..99154f5 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/or_data.conf
@@ -0,0 +1,7 @@ + 07 01 03 3A 80 00 3A 00 ff ff 00 17 03 00 07 03 + 09 00 0e 10 16 02 19 25 1a 04 1c ff 32 5e 33 15 + 35 29 36 17 4b 74 4c 64 4d 3b 50 27 61 d6 62 98 + 6b ae 6f 5b 77 f2 79 ff 7f 2d + 07 01 12 16 c0 00 ff ff ff ff 00 05 03 10 32 5c + 33 1a 6b a2 7f 20 +
diff --git a/wlan_sd8897/mapp/mlanconfig/config/pad_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/pad_cfg.conf new file mode 100644 index 0000000..80e2788 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/pad_cfg.conf
@@ -0,0 +1,11 @@ +# File : pad_cfg.conf + +## Get CFG data for PAD OR +pad_cfg_get={ + CmdCode=0x008f # do NOT change this line + Action:2=0 # 0 - GET + Type:2=5 # 5 - optimized pad reg + CfgLen:2={ + } +} +
diff --git a/wlan_sd8897/mapp/mlanconfig/config/requesttpc.conf b/wlan_sd8897/mapp/mlanconfig/config/requesttpc.conf new file mode 100644 index 0000000..690c30d --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/requesttpc.conf
@@ -0,0 +1,15 @@ +# File : requesttpc.conf + +######################### requesttpc command configuration ################## + +requesttpc={ + CmdCode=0x0060 # do NOT change this line + #DestMac: Destination STA address + DestMac:6='0x02,0x04,0x0e,0x06,0x01,0x12' + #RateIndex: IEEE Rate index to send request + RateIndex:1=22 + #Timeout: Response timeout in ms + Timeout:2=10 +} + +######################### End of requesttpc command configuration ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/robust_btc.conf b/wlan_sd8897/mapp/mlanconfig/config/robust_btc.conf new file mode 100644 index 0000000..7d5cb9b --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/robust_btc.conf
@@ -0,0 +1,189 @@ +# File : robust_btc.conf + +######################### Robust Coex command ############### +mode_get={ + CmdCode=0x00e0 # do NOT change this line + Action:2=0 # GET + RSVD:2=0 + + # Robust Coex Mode TLV + RobustCoexTlvType:2=0x0160 + RobustCoexTlvLength:2={ + Enable:1=0x00 # Read-back Coex mode(s) + Reserved:3=0 + } +} + +mode_timeshare={ + CmdCode=0x00e0 # do NOT change this line + Action:2=1 # SET + RSVD:2=0 + + # Robust Coex Mode TLV + RobustCoexTlvType:2=0x0160 + RobustCoexTlvLength:2={ + # All the modes below are mutually exclusive of each other; + Enable:1=0x01 # Bit0: Enable 2x2 or 1x1 Time Distribute(TMD) + # Robust Coex(RBC) mode, when uAP bss start, + # uAP TMD RBC scheme is enabled, + # STA TMD RBC scheme is disabled. + Reserved:3=0 + } +} + +mode_spatial={ + CmdCode=0x00e0 # do NOT change this line + Action:2=1 # SET + RSVD:2=0 + + # Robust Coex Mode TLV + RobustCoexTlvType:2=0x0160 + RobustCoexTlvLength:2={ + # All the modes below are mutually exclusive of each other; + Enable:1=0x82 # Bit1: Enable 1x1 SMPS Spatial RBC Mode, e.g. 0x02 + # Bit7: Enable uAP+STA SMPS RBC Mode, + # when uAP bss start, uAP SMPS RBC scheme enable, + # must combined with BIT1 or BIT2, e.g. 0x82, 0x84. + Reserved:3=0 + } +} + +mode_none={ + CmdCode=0x00e0 # do NOT change this line + Action:2=1 # SET + RSVD:2=0 + + # Robust Coex Mode TLV + RobustCoexTlvType:2=0x0160 + RobustCoexTlvLength:2={ + Enable:1=0 # Concurrent Coex mode. Used for chips which has + # separate antenna for BT + Reserved:3=0 + } +} + +mode_2={ + CmdCode=0x00e0 # do NOT change this line + Action:2=1 # SET + RSVD:2=0 + + # Robust Coex Mode TLV + RobustCoexTlvType:2=0x0160 + RobustCoexTlvLength:2={ + Enable:1=0x20 # Concurrent Coex mode with Tx power control and Rx De-sense. + # Used for chips which has separate antenna for BT + Reserved:3=0 + } +} + +gpio_cfg={ + CmdCode=0x00e0 # do NOT change this line + Action:2=1 # SET + RSVD:2=0 + + # Robust Coex Mode TLV + RobustCoexTlvType:2=0x021B + RobustCoexTlvLength:2={ + Enable:1=0x1 # enable GPIO cfg for external bt request + gpionum:1=4 # gpio 4 + gpiopolarity:1=1 # Polarity High + } +} + +#In Station generic case +#BT time is set as BTTime +#Wlan time is set as Wlan Time +generictime={ + CmdCode=0x00e0 + Action:2=1 + RSVD:2=0 + RobustCoexTlvType:2=0x0390 + RobustCoexTlvLength:2={ + Enable:2=0x01 + BtTime:2=10000 #(10ms) BTTime must be less than 65535 + WlanTime:2=40000 #(40ms) WlanTime must be less than 65535 + } +} + +#In Station A2DP case +#BT time is set as BTTime +#Wlan time is set as Wlan Time +a2dptime={ + CmdCode=0x00e0 + Action:2=1 + RSVD:2=0 + RobustCoexTlvType:2=0x0391 + RobustCoexTlvLength:2={ + Enable:2=0x01 + BtTime:2=10000 #(10ms) BTTime must be less than 65535 + WlanTime:2=39500 #(39.5ms) WlanTime must be less than 65535 + } +} + +#In Station inquiry case +#BT time is set as BTTime +#Wlan time is set as Wlan Time +inquirytime={ + CmdCode=0x00e0 + Action:2=1 + RSVD:2=0 + RobustCoexTlvType:2=0x0392 + RobustCoexTlvLength:2={ + Enable:2=0x01 + BtTime:2=21215 #(21.215ms) BTTime must be less than 65535 + WlanTime:2=11000 #(11ms) WlanTime must be less than 65535 + } +} + +#In Ap generic case +#BT time is BTTimeBusy when BT has traffic +#BT time is BTTimeIdle when BT is idle +#Wlan time is WlanTimeBusy when Wlan has traffic +#Wlan time is WlanTimeIdle when Wlan is idle +ap_generictime={ + CmdCode=0x00e0 + Action:2=1 + RSVD:2=0 + RobustCoexTlvType:2=0x0393 + RobustCoexTlvLength:2={ + Enable:2=0x01 + BtTime_MAX:2=23000 #(23ms) BTTime(BT Busy) must be less than 28767 + BtTime_MIN:2=6500 #(6.5ms) BTTime(BT Idle) must be less than 28767 + WlanTime_MAX:2=18000 #(18ms) WlanTime(Wlan Busy) must be less than 32767 + WlanTime_MIN:2=5750 #(5.75ms) WlanTime(Wlan Idle) must be less than 32767 + } +} + +#In Ap A2DP case +#BT time is change from BTTimeMax to BTTimeMin +#Wlan time is change from WlanTimeMax to WlanTimeMin +ap_a2dptime={ + CmdCode=0x00e0 + Action:2=1 + RSVD:2=0 + RobustCoexTlvType:2=0x0394 + RobustCoexTlvLength:2={ + Enable:2=0x01 + BtTimebusy:2=23000 #(23ms) Maximum BTTime must be less than 32767 + BtTimeidle:2=6500 #(6.5ms) Minimum BTTime must be less than 32767 + WlanTimebusy:2=18000 #(18ms) Maximum WlanTime must be less than 32767 + WlanTimeidle:2=5750 #(5.75ms) Minimum WlanTime must be less than 32767 + } +} + +#In Ap inquiry case +#BT time is set as BTTime +#Wlan time is set as Wlan Time +ap_inquirytime={ + CmdCode=0x00e0 + Action:2=1 + RSVD:2=0 + RobustCoexTlvType:2=0x0395 + RobustCoexTlvLength:2={ + Enable:2=0x01 + BtTime:2=28750 #(28.75ms) BTTime must less than 32767 + WlanTime:2=20000 #(20ms) WlanTime must be less than 32767 + } +} + +######################### Robust Coex command ###############
diff --git a/wlan_sd8897/mapp/mlanconfig/config/sdio_pulldown.conf b/wlan_sd8897/mapp/mlanconfig/config/sdio_pulldown.conf new file mode 100644 index 0000000..745978b --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/sdio_pulldown.conf
@@ -0,0 +1,27 @@ +# File : sdio_pulldown.conf + +######################### SDIO Pulldown command ############### +sdio_pulldown_get={ + CmdCode=0x0093 # do NOT change this line + + Action:2=0 # GET + PullUpDelay:2=0 + PullDownDelay:2=0 +} + +sdio_pulldown_set={ + CmdCode=0x0093 # do NOT change this line + + Action:2=1 # SET + PullUpDelay:2=0 + PullDownDelay:2=0 +} + +sdio_pulldown_disable={ + CmdCode=0x0093 # do NOT change this line + + Action:2=1 # SET + PullUpDelay:2=0xffff + PullDownDelay:2=0xffff +} +######################### SDIO Pulldown command ###############
diff --git a/wlan_sd8897/mapp/mlanconfig/config/small_debug.conf b/wlan_sd8897/mapp/mlanconfig/config/small_debug.conf new file mode 100644 index 0000000..1457426 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/small_debug.conf
@@ -0,0 +1,62 @@ +# File : small_debug.conf + +######################### DBG Config command ############### +small_debug_enable={ + CmdCode=0x008b # do NOT change this line + + Dst:1=0x06 # dbgs_cfg_t + Air_Chan:1=0x00 + RSVD:1=0 + Num_Entries:1=0x81 # one entry global enable + + # dbgs_t + En_Mask:2=0x0000 # en_mask_id + BaseId:2=0x0000 # base_id +} + +small_debug_disable={ + CmdCode=0x008b # do NOT change this line + + Dst:1=0x00 + Air_Chan:1=0x00 + RSVD:1=0 + Num_Entries:1=0x00 +} + +######################### DBG Get Trace Memory ############### +# Disable Air and Host destinations to disable dbgs_drain +# Enable only the Power save ID - Host interface is a course enable - not on specific event +trace_enable={ + CmdCode=0x008b # do NOT change this line + + Dst:1=0x00 # dbgs_cfg_t - don't drain debug + Air_Chan:1=0x00 + RSVD:1=0 + Num_Entries:1=0x02 # 2 entry + + # dbgs_t + En_Mask:2=0x5fff # en_mask_id - enable single ID=0x80 + BaseId:2=0x0020 # base_id + En_Mask:2=0x5fff # en_mask_id - enable single ID=0x80 + BaseId:2=0x0021 # base_id +} + +get_trace={ + CmdCode=0x008c # do NOT change this line + + startAddress:4=0x02 # read trace memeory - not a generic memory read + len:2=0x0000 # First read=0 - for next reads use the returned next start +} + +trace_disable={ + CmdCode=0x008b # do NOT change this line + + Dst:1=0x00 # dbgs_cfg_t - don't drain debug + Air_Chan:1=0x00 + RSVD:1=0 + Num_Entries:1=0x01 # one entry + + # dbgs_t + En_Mask:2=0x0000 # en_mask_id + BaseId:2=0x0000 # base_id +}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/smc.conf b/wlan_sd8897/mapp/mlanconfig/config/smc.conf new file mode 100644 index 0000000..9bead4c --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/smc.conf
@@ -0,0 +1,124 @@ +# File : smc.conf + +smc_get={ + CmdCode=0x012d #do not change this + + Action:2=0 # GET +} + +smc_set={ + CmdCode=0x012d #do not change this + + Action:2=1 # SET + +# SSID + SSIDHeaderType:2=0x0000 + SSIDHeaderLen:2={ + SSID:10="Marvell_SMC_1" + } + +# Beacon Period. +# This should be smaller than than MinScanTime. + BeaconHeaderType:2=0x012c + BeaconHeaderLen:2={ + beaconPeriod:2=30 + } + +# Channel list. +# ChanNumber, MinScanTime and ScanTime are mandatory. +# MinScanTime is minimum dwelling time for ChanNumber channel. +# ScanTime is maximum dwelling time for ChanNumber channel. + ChannHeaderType:2=0x0101 + ChannHeaderLen:2={ + +# Following four line define one channel. +# Please add similar four lines with different channel number for new channel. + Chan1_RadioType:1=0 + Chan1_ChanNumber:1=1 + Chan1_ScanType:1=2 + Chan1_MinScanTime:2=40 + Chan1_ScanTime:2=200 + + Chan2_RadioType:1=0 + Chan2_ChanNumber:1=6 + Chan2_ScanType:1=3 + Chan2_MinScanTime:2=40 + Chan2_ScanTime:2=200 + + Chan3_RadioType:1=0 + Chan3_ChanNumber:1=9 + Chan3_ScanType:1=2 + Chan3_MinScanTime:2=40 + Chan3_ScanTime:2=100 + + Chan2_RadioType:1=0 + Chan2_ChanNumber:1=11 + Chan2_ScanType:1=3 + Chan2_MinScanTime:2=40 + Chan2_ScanTime:2=200 + } + +#Custom IE +#Currently max size of custom IE supported is 50 bytes. +# +# CustomHeaderType:2=0x010a +# CustomHeaderLen:2={ +# start:1=0xdd +# start:1=0x10 +# start:1=0x00 +# start:1=0x01 +# start:1=0x02 +# start:1=0x03 +# start:1=0x04 +# start:1=0x05 +# start:1=0x06 +# start:1=0x07 +# start:1=0x08 +# start:1=0x09 +# start:1=0x0a +# start:1=0x0b +# start:1=0x0c +# start:1=0x0d +# start:1=0x0e +# start:1=0x0f +# } + +#Multicast mac filtering address +#All the multicast packets from starting mac address to ending mac address would +#be captured and sent to the host. + MACHeaderType:2=0x01cc + MACHeaderLen:2={ +#Staring Multicast mac address + start:1=0x01 + start:1=0x00 + start:1=0x5e + start:1=0x00 + start:1=0x00 + start:1=0x01 + +#Ending Multicast mac address + end:1=0x01 + end:1=0x00 + end:1=0x5e + end:1=0x7f + end:1=0xff + end:1=0xff + +#FilterType +# 1 for RX AP frames +# 2 for RX STA frames +# 3 for both + Filter:2=0x3 + } + +} +smc_start={ + CmdCode=0x012d #do not change this + + Action:2=2 # START +} +smc_stop={ + CmdCode=0x012d #do not change this + + Action:2=3 # STOP +}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/subevent.conf b/wlan_sd8897/mapp/mlanconfig/config/subevent.conf new file mode 100644 index 0000000..ac7f333 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/subevent.conf
@@ -0,0 +1,103 @@ +# File : subevent.conf + +######################### Subscribe Events command ################## +subevent_get={ + CmdCode=0x0075 # do NOT change this line + + Action:2=0 # GET + Events:2=0 +} + +subevent_set={ + CmdCode=0x0075 # do NOT change this line + + Action:2=1 # SET + Events:2=0xbc8 # bit0 - Beacon RSSI_LOW + # bit1 - Beacon SNR_LOW + # bit2 - FAILED_COUNT + # bit3 - Beacon Missed + # bit4 - Beacon RSSI_HIGH + # bit5 - Beacon SNR_HIGH + # bit6 - Data RSSI_LOW + # bit7 - Data SNR_LOW + # bit8 - Data RSSI_HIGH + # bit9 - Data SNR_HIGH + # bit10 - LINK_QUALITY + # bit11 - PRE_BCN_LOST + # bit12-15 - Reserved + + LowRssiTlvType:2=0x0104 + LowRssiTlvLength:2={ + Threshold:1=70 + ReportingFreq:1=0 + } + + LowSnrTlvType:2=0x0105 + LowSnrTlvLength:2={ + Threshold:1=56 + ReportingFreq:1=0 + } + + FailedCountTlvType:2=0x0106 + FailedCountTlvLength:2={ + Threshold:1=5 + ReportingFreq:1=0 + } + + BeaconMissTlvType:2=0x0107 + BeaconMissTlvLength:2={ + BeaconMissed:1=60 + Reserved:1=0 + } + + HighRssiTlvType:2=0x0116 + HighRssiTlvLength:2={ + Threshold:1=40 + ReportingFreq:1=0 + } + + HighSnrTlvType:2=0x0117 + HighSnrTlvLength:2={ + Threshold:1=86 + ReportingFreq:1=0 + } + + DataLowRssiTlvType:2=0x0126 + DataLowRssiTlvLength:2={ + Threshold:1=10 + ReportingFreq:1=0 + } + + DataLowSnrTlvType:2=0x0127 + DataLowSnrTlvLength:2={ + Threshold:1=66 + ReportingFreq:1=0 + } + + DataHighRssiTlvType:2=0x0128 + DataHighRssiTlvLength:2={ + Threshold:1=50 + ReportingFreq:1=0 + } + + DataHighSnrTlvType:2=0x0129 + DataHighSnrTlvLength:2={ + Threshold:1=96 + ReportingFreq:1=1 + } + LinkQualityTlvType:2=0x0124 + LinkQualityTlvType:2={ + LinkSNRThreshold:2=0x0056 + LinkSNRFrequency:2=0x0003 + MinRateVal:2=0x0014 + MinRateFreq:2=0x0003 + TxLatencyVal:4=0x00C8 + TxLatencyThreshold:4=0x0003 + } + PreBcnLostTlvType:2=0x0149 + PreBcnLostTlvLength:2={ + PreBeaconCnt:1=30 + Reserved:1=0 + } +} +######################### Subscribe Events command ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/tdls.conf b/wlan_sd8897/mapp/mlanconfig/config/tdls.conf new file mode 100644 index 0000000..d7369cf --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/tdls.conf
@@ -0,0 +1,118 @@ +# File : tdls.conf + +######################### TDLS commands configuration ################## +# starts with a command name with parameters embedded inside + +tdls_setinfo={ + CapInfo=0x2421 + Rate=0x02,0x04,0x0b,0x16,0x0C,0x12,0x18,0x24,0x30,0x48,0x60,0x6c + QosInfo=0x0F # 1 byte qos capability field. Valid values 0x00 or 0x0F + # For AMPDU over direct link, this should be 0x00 + ExtendCapabilities=0x00,0x00,0x00,0x50,0x20 # Enable Peer U-APSD(Bit28), TDLS channel switch(Bit30), TDLS(Bit37) + HTCapability={ + HTCapabilityInfo=0x62 # 2 byte HT capability field. + AMPDUParam=0x3 + SupportedMCSSet=0xFF,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + HTExtCapability=0x00 + TxBfCapability=0x00 + AntennaSel=0x00 + } + HTInformation={ + PrimaryChannel=1 + Field2=0x0 + Field3=0x5 + Field4=0x0 + BasicMCSSet=0xFF,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 + # 16 elements as per the structure. + } + 2040BSSCoex=0x01 # 1 byte 2040 Coex field. + VHTCapability={ + VHTCapabilityInfo=0x33C00000 # 4 byte VHT capability field. + RxMCSMap=0xFFFC + RxMaxRate=0x0000 #bit 29-31 reserved + TxMCSMap=0xFFFC + TxMaxRate=0x0000 #bit 61-63 reserved + } + VHTOper={ + ChanWidth=0x01; + ChanCF1=0x01; + ChanCF2=0x01; + BasicMCSMap=0xFF, 0XFF; #Basic MCS set map, each 2 bits stands for a Nss + } +# Uncomment the RSNInfo while testing open-secure network. + RSNInfo={ + GroupCipherSuite=0x00,0x0F,0xAC,0x07 + PairwiseCipherCount=1 + PairwiseCipherSuite=0x00,0x0F,0xAC,0x04 # CCMP + #PairwiseCipherSuite=0x00,0x0F,0xAC,0x02 # TKIP multiple Pairwise Cipher Suite if count > 1. + AKMSuiteCount=1 + AKMSuite=0x00,0x0F,0xAC,0x07 # multiple AKM Suite if count > 1. + #AKMSuite=0x00,0x0F,0xAC,0x07 # multiple AKM Suite if count > 1. + RSNCapability=0x200 # Enable PeerKey + PMKIDCount=0 + } + SupportedChannels={ + FirstChannelNo=1 + NumberofSubBandChannels=11 + # multiple instances of First Channel No, Number of SubBand Channels + } + SupportedRegulatoryClasses={ + CurrentRegulatoryClass=1 + NumofRegulatoryClasses=15 + ListOfRegulatoryClasses=1,2,3,4,12,22,23,24,25,27,28,29,30,32,33 + } + +# CountryInfo={ +# CountryString="US" # 3 byte country string +# FirstChannel=1 +# NumberofChannels=11 +# TxPower=30 +# # multiple instances of First Channel, Number of Channels, Tx Power +# } +} + +tdls_discovery={ + PeerMAC=00:50:43:27:B0:41 # MAC address of Peer +} + +tdls_setup={ + PeerMAC=00:50:43:27:B0:41 # MAC address of Peer + WaitTimems=3000 # Time in milliseconds to wait + # for setup response from peer + KeyLifetime=301 # Lifetime of security key in seconds. +} + +tdls_teardown={ + PeerMAC=00:50:43:27:B0:41 # MAC address of Peer + ReasonCode=26 # Tear down reason code +} + +tdls_channel_switch={ + PeerMAC=00:50:43:27:B0:41 # MAC address of Peer + Band=0 # Band + PrimaryChannel=6 # Primary channel no for channel switching + SecondaryChannelOffset=0 # Secondary channel offset + Periodicity=1 # Channel switch periodicity + ChannelSwitchTime=10 # Channel switch time in ms. + ChannelSwitchTimeout=14 # Channel switch timeout in ms + RegulatoryClass=12 # Target regulatory class +} + +tdls_cs_params={ + UnitTime=2 # Unit time, multiples of 10 ms + ThresholdOtherLink=10 # Threshold for other link + ThresholdDirectLink=1 # Threshold for direct link +} + + +tdls_stop_channel_switch={ + PeerMAC=00:50:43:27:B0:41 # MAC address of Peer +} + +tdls_powermode={ + PeerMAC=00:50:43:27:B0:41 # MAC address of Peer + PowerMode=1 # 0: Active, 1 : Power Save +} + + +######################### TDLS commands configuration ##################
diff --git a/wlan_sd8897/mapp/mlanconfig/config/tspecs.conf b/wlan_sd8897/mapp/mlanconfig/config/tspecs.conf new file mode 100644 index 0000000..86c1ac6 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/tspecs.conf
@@ -0,0 +1,100 @@ +# TSPEC contents for TID=0, UserPriority = 6 +[tspec0] +# Element ID +dd +# Length +3d +# OUI +00 50 f2 +# OUI Type +02 +# OUI SubType +02 +# Version +01 +# TSInfo +e0 34 00 +# Nominal MSDU Size +d0 80 +# Maximum MSDU Size +d0 00 +# Min Service Interval +20 4e 00 00 +# Max Service Interval +20 4e 00 00 +# Inactivity Interval +80 96 98 00 +# Suspension Interval +ff ff ff ff +# Service Start Time +00 00 00 00 +# Minimum Data Rate +00 45 01 00 +# Mean Data Rate +00 45 01 00 +# Peak Data Rate +00 45 01 00 +# Max Burst Size +00 00 00 00 +# Delay Bound +00 00 00 00 +# Min PHY Rate +00 1b b7 00 +# Surplus Bandwidth Allowance +00 30 +# Medium Time +00 00 +# Extra Data Bytes +[/tspec0] + + +# TSPEC contents for TID=1, UserPriority = 4 +[tspec1] +# Element ID +dd +# Length +3d +# OUI +00 50 f2 +# OUI Type +02 +# OUI SubType +02 +# Version +01 +# TSInfo +e3 20 00 +# Nominal MSDU Size +96 00 +# Maximum MSDU Size +dc 05 +# Min Service Interval +00 00 00 00 +# Max Service Interval +00 00 00 00 +# Inactivity Interval +00 00 00 00 +# Suspension Interval +ff ff ff ff +# Service Start Time +00 00 00 00 +# Minimum Data Rate +a0 00 00 00 +# Mean Data Rate +a0 00 00 00 +# Peak Data Rate +a0 00 00 00 +# Max Burst Size +00 00 00 00 +# Delay Bound +00 00 00 00 +# Min PHY Rate +80 8d 5b 00 +# Surplus Bandwidth Allowance +00 30 +# Medium Time +00 00 +# Extra Data Bytes +dd 04 00 50 43 ff +[/tspec1] +
diff --git a/wlan_sd8897/mapp/mlanconfig/config/turbo_mode.conf b/wlan_sd8897/mapp/mlanconfig/config/turbo_mode.conf new file mode 100644 index 0000000..8a92f2f --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/turbo_mode.conf
@@ -0,0 +1,9 @@ +#File : turbo_mode.conf +## WMM turbo mode config command +turbo_mode_set={ + CmdCode=0x0016 # do NOT change this line + Action:2=1 # 1 - SET + OID:2=0x27 # OID_WMM_TURBO_MODE + Size:2=128 + Value:128=5 # 1 -- Enable turbo_mode_1; 2 -- Enable turbo_mode_2; otherwise, disable turbo_mode +}
diff --git a/wlan_sd8897/mapp/mlanconfig/config/txpwrlimit_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/txpwrlimit_cfg.conf new file mode 100644 index 0000000..2192fd5 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/txpwrlimit_cfg.conf
@@ -0,0 +1,610 @@ +# File : txpwrlimit_cfg.conf +## Get CFG data for Tx power limitation +txpwrlimit_2g_cfg_get={ + CmdCode=0x00fb # do NOT change this line + Action:2=0 # 0 - GET + SubBand:2=0x00 # 0x00 2G subband (2.4G: channel 1-14) + # 0x10 5G subband0 (5G: channel 36,40,44,48, + # 52,56,60,64) + # 0x11 5G subband1 (5G: channel 100,104,108,112, + # 116,120,124,128, + # 132,136,140,144) + # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172) + # 0x13 5G subband3 (5G: channel 183,184,185,187,188, + # 189, 192,196; + # 5G: channel 7,8,11,12,16,34) +} + + +txpwrlimit_5g_cfg_get_sub0={ + CmdCode=0x00fb # do NOT change this line + Action:2=0 # 0 - GET + SubBand:2=0x10 # 0x00 2G subband (2.4G: channel 1-14) + # 0x10 5G subband0 (5G: channel 36,40,44,48, + # 52,56,60,64) + # 0x11 5G subband1 (5G: channel 100,104,108,112, + # 116,120,124,128, + # 132,136,140,144) + # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172) + # 0x13 5G subband3 (5G: channel 183,184,185,187,188, + # 189, 192,196; + # 5G: channel 7,8,11,12,16,34) +} + + +txpwrlimit_5g_cfg_get_sub1={ + CmdCode=0x00fb # do NOT change this line + Action:2=0 # 0 - GET + SubBand:2=0x11 # 0x00 2G subband (2.4G: channel 1-14) + # 0x10 5G subband0 (5G: channel 36,40,44,48, + # 52,56,60,64) + # 0x11 5G subband1 (5G: channel 100,104,108,112, + # 116,120,124,128, + # 132,136,140,144) + # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172) + # 0x13 5G subband3 (5G: channel 183,184,185,187,188, + # 189, 192,196; + # 5G: channel 7,8,11,12,16,34) +} + + +txpwrlimit_5g_cfg_get_sub2={ + CmdCode=0x00fb # do NOT change this line + Action:2=0 # 0 - GET + SubBand:2=0x12 # 0x00 2G subband (2.4G: channel 1-14) + # 0x10 5G subband0 (5G: channel 36,40,44,48, + # 52,56,60,64) + # 0x11 5G subband1 (5G: channel 100,104,108,112, + # 116,120,124,128, + # 132,136,140,144) + # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172) + # 0x13 5G subband3 (5G: channel 183,184,185,187,188, + # 189, 192,196; + # 5G: channel 7,8,11,12,16,34) +} + + +txpwrlimit_5g_cfg_get_sub3={ + CmdCode=0x00fb # do NOT change this line + Action:2=0 # 0 - GET + SubBand:2=0x13 # 0x00 2G subband (2.4G: channel 1-14) + # 0x10 5G subband0 (5G: channel 36,40,44,48, + # 52,56,60,64) + # 0x11 5G subband1 (5G: channel 100,104,108,112, + # 116,120,124,128, + # 132,136,140,144) + # 0x12 5G subband2 (5G: channel 149,153,157,161,165,172) + # 0x13 5G subband3 (5G: channel 183,184,185,187,188, + # 189, 192,196; + # 5G: channel 7,8,11,12,16,34) +} + +## Set CFG data for Tx power limitation +## +## TLVStartFreq: Starting Frequency of the band for this channel +## 2407, 2414 or 2400 for 2.4 GHz +## 5000 +## 4000 +## TLVChanWidth: Channel Width +## 20 +## TLVChanNum : Channel Number +## TLVPwr[] : ModulationGroup +## 0: CCK (1,2,5.5,11 Mbps) +## 1: OFDM (6,9,12,18 Mbps) +## 2: OFDM (24,36 Mbps) +## 3: OFDM (48,54 Mbps) +## 4: HT20 (MCS0,1,2) +## 5: HT20 (MCS3,4) +## 6: HT20 (MCS5,6,7) +## 7: HT40 (MCS0,1,2) +## 8: HT40 (MCS3,4) +## 9: HT40 (MCS5,6,7) +## 10: HT2_20 (MCS8,9,10) +## 11: HT2_20 (MCS11,12) +## 12: HT2_20 (MCS13,14,15) +## 13: HT2_40 (MCS8,9,10) +## 14: HT2_40 (MCS11,12) +## 15: HT2_40 (MCS13,14,15) +## 16: VHT_QAM256 (MCS8) +## 17: VHT_40_QAM256 (MCS8,9) +## 18: VHT_80_PSK (MCS0,1,2) +## 19: VHT_80_QAM16 (MCS3,4) +## 20: VHT_80_QAM64 (MCS5,6,7) +## 21: VHT_80_QAM256 (MCS8,9) +## 22: VHT2_20_QAM256 (MCS8,9) +## 23: VHT2_40_QAM256 (MCS8,9) +## 24: VHT2_80_PSK (MCS0, 1, 2) +## 25: VHT2_80_QAM16 (MCS3,4) +## 26: VHT2_80_QAM64 (MCS5,6,7) +## 27: VHT2_80_QAM256 (MCS8,9) +## Power Limit in dBm +## +## Note: For KF, add VHT 20/40/80 1SS/2SS mod group. + +## 2G subband0 Tx power limit CFG +txpwrlimit_2g_cfg_set={ + CmdCode=0x00fb # do NOT change this line + Action:2=1 # 1 - SET + SubBand:2=0 # do NOT use this member in set cmd + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=1 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=2 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=3 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=4 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=5 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=6 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=7 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=8 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=9 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=10 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=11 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=12 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=13 + TLVPwr:32='0,17,1,15,2,15,3,13,4,15,5,15,6,13,7,15,8,15,9,13,10,15,11,15,12,15,13,15,14,15,15,15' + } + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=2407 + TLVChanWidth:1=20 + TLVChanNum:1=14 + TLVPwr:32='0,12,1,12,2,12,3,12,4,12,5,12,6,12,7,12,8,12,9,12,10,12,11,12,12,12,13,12,14,12,15,12' + } +} + + +## 5G subband1 Tx power limit CFG +txpwrlimit_5g_cfg_set_sub0={ + CmdCode=0x00fb # do NOT change this line + Action:2=1 # 1 - SET + SubBand:2=0 # do NOT use this member in set cmd + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=36 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=40 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=44 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=48 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=52 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=56 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=60 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=64 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } +} + +## 5G subband2 Tx power limit CFG +txpwrlimit_5g_cfg_set_sub1={ + CmdCode=0x00fb # do NOT change this line + Action:2=1 # 1 - SET + SubBand:2=0 # do NOT use this member in set cmd + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=100 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=104 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=108 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=112 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=116 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=120 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=124 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=128 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=132 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=136 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=140 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=144 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + +} + + +## 5G subband3 Tx power limit CFG +txpwrlimit_5g_cfg_set_sub2={ + CmdCode=0x00fb # do NOT change this line + Action:2=1 # 1 - SET + SubBand:2=0 # do NOT use this member in set cmd + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=149 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=153 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=157 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=161 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=165 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } +} + + +## 5G subband4 Tx power limit CFG +txpwrlimit_5g_cfg_set_sub3={ + CmdCode=0x00fb # do NOT change this line + Action:2=1 # 1 - SET + SubBand:2=0 # do NOT use this in set cmd + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=183 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=184 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=185 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=187 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=188 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=189 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=192 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=4000 + TLVChanWidth:1=20 + TLVChanNum:1=196 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=7 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=8 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=11 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=12 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=16 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } + + + ChanTRPC.TlvType:2=0x0189 + ChanTRPC.TlvLength:2={ + TLVStartFreq:2=5000 + TLVChanWidth:1=20 + TLVChanNum:1=34 + TLVPwr:56='0,17,1,15,2,15,3,11,4,15,5,15,6,11,7,15,8,15,9,11,10,15,11,15,12,14,13,15,14,15,15,14,16,11,17,11,18,13,19,13,20,10,21,10,22,11,23,11,24,13,25,13,26,12,27,10' + } +} +
diff --git a/wlan_sd8897/mapp/mlanconfig/config/txrate_cfg.conf b/wlan_sd8897/mapp/mlanconfig/config/txrate_cfg.conf new file mode 100644 index 0000000..4d2f789 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/config/txrate_cfg.conf
@@ -0,0 +1,171 @@ +# File : txrate_cfg.conf + +## Tx Rate Configuration command +txrate_cfg_get={ + CmdCode=0x00d6 # do NOT change this line + Action:2=0 # 0 - GET + Index:2=0 # do NOT change this line + + TxRateScope.TlvType:2=0x0153 + TxRateScope.TlvLength:2={ + } +} + +txrate_cfg_set_bg={ + CmdCode=0x00d6 # do NOT change this line + + Action:2=1 # 1 - SET + Index:2=0 # do NOT change this line + + TxRateScope.TlvType:2=0x0153 + TxRateScope.TlvLength:2={ + ################# TXRATE SCOPE ###################### + + # The following table shows the bitmap of the rates: + # (bit 0 is the least significant bit) + # Bit Data rate + # 0 1 Mbps + # 1 2 Mbps + # 2 5.5 Mbps + # 3 11 Mbps + # 4 Reserved + HRDSSS.RateScope:2=0x0000 + + # The following table shows the bitmap of the rates: + # (bit 0 is the least significant bit) + # Bit Data rate + # 0 6 Mbps + # 1 9 Mbps + # 2 12 Mbps + # 3 18 Mbps + # 4 24 Mbps + # 5 36 Mbps + # 6 48 Mbps + # 7 54 Mbps + OFDM.RateScope:2=0x0080 + + # The following table shows the bitmap of the rates: + # (bit 0 is the least significant bit) + # Bit Data rate + # 0 MCS0 + # 1 MCS1 + # 2 MCS2 + # 3 MCS3 + # 4 MCS4 + # 5 MCS5 + # 6 MCS6 + # 7 MCS7 + # 32 MCS32 + HT.RateScopeDword0:4=0x00000000 + HT.RateScopeDword1:4=0x00000000 + HT.RateScopeDword2:4=0x00000000 + HT.RateScopeDword3:4=0x00000000 + } + + TxRateDrop.TlvType:2=0x0151 + TxRateDrop.TlvLength:2={ + RateDrop.Mode:4=0x00000001 + } +} + +txrate_cfg_set_bgn={ + CmdCode=0x00d6 # do NOT change this line + + Action:2=1 # 1 - SET + Index:2=0 # do NOT change this line + + TxRateScope.TlvType:2=0x0153 + TxRateScope.TlvLength:2={ + ################# TXRATE SCOPE ###################### + + # The following table shows the bitmap of the rates: + # (bit 0 is the least significant bit) + # Bit Data rate + # 0 1 Mbps + # 1 2 Mbps + # 2 5.5 Mbps + # 3 11 Mbps + # 4 Reserved + HRDSSS.RateScope:2=0x0000 + + # The following table shows the bitmap of the rates: + # (bit 0 is the least significant bit) + # Bit Data rate + # 0 6 Mbps + # 1 9 Mbps + # 2 12 Mbps + # 3 18 Mbps + # 4 24 Mbps + # 5 36 Mbps + # 6 48 Mbps + # 7 54 Mbps + OFDM.RateScope:2=0x0000 + + # The following table shows the bitmap of the rates: + # (bit 0 is the least significant bit) + # Bit Data rate + # 0 MCS0 + # 1 MCS1 + # 2 MCS2 + # 3 MCS3 + # 4 MCS4 + # 5 MCS5 + # 6 MCS6 + # 7 MCS7 + # 32 MCS32 + HT.RateScopeDword0:4=0x00000080 + HT.RateScopeDword1:4=0x00000000 + HT.RateScopeDword2:4=0x00000000 + HT.RateScopeDword3:4=0x00000000 + } + + TxRateDrop.TlvType:2=0x0151 + TxRateDrop.TlvLength:2={ + RateDrop.Mode:4=0x00000001 + } +} + +########supported BasicRate setting########### +basic_rate_get={ + CmdCode=0x00d6 # do NOT change this line + Action:2=0 # 0 - GET + Index:2=0 # do NOT change this line + + TlvType:2=0x21a + TlvLength:2={ + } +} + +basic_rate_set={ + CmdCode=0x00d6 # do NOT change this line + + Action:2=1 # 1 - SET + Index:2=0 # do NOT change this line + + TlvType:2=0x21a + TlvLength:2={ + BasicRateSupport:2=0x000f #defalt value + + # The following table shows the bitmap of the rates: + # (bit 0 is the least significant bit) + # Bit BasicRateSupport + # 0 DBPSK1Mbps + # 1 DQPSK2Mbps + # 2 CCK5_5Mbps + # 3 CCK11Mbps + # 4 Not used. + # 5 OFDM6Mbps + # 6 OFDM9Mbps + # 7 OFDM12Mbps + # 8 OFDM18Mbps + # 9 OFDM24Mbps + # 10 OFDM36Mbps + # 11 OFDM48Mbps + # 12 OFDM54Mbps + # 13-15 Reserved + #note: value0x0 represents no setting value + #For example: + #BasicRateSupport:2=0x3 set supported BasicRate to DBPSK1Mbps, DQPSK2Mbps + #BasicRateSupport:2=0x180 set supported BasicRate to OFDM18Mbps, OFDM12Mbps + } +}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanconfig.c b/wlan_sd8897/mapp/mlanconfig/mlanconfig.c new file mode 100644 index 0000000..b876af1 --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/mlanconfig.c
@@ -0,0 +1,4842 @@ +/** @file mlanconfig.c + * + * @brief Program to configure addition parameters into the mlandriver + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 11/26/2008: initial version + 03/10/2009: add setuserscan, getscantable etc. commands + 08/11/2009: add addts, regclass, setra, scanagent etc. commands +************************************************************************/ + +#include "mlanconfig.h" +#include "mlanhostcmd.h" +#include "mlanmisc.h" + +/** mlanconfig version number */ +#define MLANCONFIG_VER "M2.0" + +/** Initial number of total private ioctl calls */ +#define IW_INIT_PRIV_NUM 128 +/** Maximum number of total private ioctl calls supported */ +#define IW_MAX_PRIV_NUM 1024 + +/******************************************************** + Local Variables +********************************************************/ + +/** Private ioctl commands */ +enum COMMANDS { + CMD_HOSTCMD, + CMD_MEFCFG, + CMD_ARPFILTER, + CMD_CFG_DATA, + CMD_CMD52RW, + CMD_CMD53RW, + CMD_GET_SCAN_RSP, + CMD_SET_USER_SCAN, + CMD_ADD_TS, + CMD_DEL_TS, + CMD_QCONFIG, + CMD_QSTATS, + CMD_TS_STATUS, + CMD_WMM_QSTATUS, + CMD_REGRW, + CMD_MEMRW, + CMD_STA_CUSTOM_IE, + CMD_STA_MGMT_FRAME_TX, + CMD_TDLS_CONF, + CMD_TDLS_INFO, + CMD_TDLS_DISCOVERY, + CMD_TDLS_SETUP, + CMD_TDLS_TEARDOWN, + CMD_TDLS_POWERMODE, + CMD_TDLS_LINK_STATUS, + CMD_TDLS_DEBUG, + CMD_TDLS_CHANNEL_SWITCH, + CMD_TDLS_STOP_CHAN_SWITCH, + CMD_TDLS_CS_PARAMS, + CMD_TDLS_CS_DISABLE, +}; + +static t_s8 *commands[] = { + "hostcmd", + "mefcfg", + "arpfilter", + "cfgdata", + "sdcmd52rw", + "sdcmd53rw", + "getscantable", + "setuserscan", + "addts", + "delts", + "qconfig", + "qstats", + "ts_status", + "qstatus", + "regrdwr", + "memrdwr", + "customie", + "mgmtframetx", + "tdls_config", + "tdls_setinfo", + "tdls_discovery", + "tdls_setup", + "tdls_teardown", + "tdls_powermode", + "tdls_link_status", + "tdls_debug", + "tdls_channel_switch", + "tdls_stop_channel_switch", + "tdls_cs_params", + "tdls_disable_cs", +}; + +static t_s8 *usage[] = { + "Usage: ", + " mlanconfig -v (version)", + " mlanconfig <mlanX> <cmd> [...]", + " where", + " mlanX : wireless network interface", + " cmd : hostcmd", + " : mefcfg", + " : customie", + " : mgmtframetx", + " : arpfilter", + " : tdls_config", + " : tdls_setinfo", + " : tdls_discovery", + " : tdls_setup", + " : tdls_teardown", + " : tdls_powermode", + " : tdls_link_status", + " : tdls_debug", + " : tdls_channel_switch", + " : tdls_stop_channel_switch", + " : tdls_cs_params", + " : tdls_disable_cs", + " : cfgdata", + " : sdcmd52rw, sdcmd53rw", + " : getscantable, setuserscan", + " : addts, delts, qconfig, qstats, ts_status, qstatus", + " : regrdwr, memrdwr", + " : additional parameter for hostcmd", + " : <filename> <cmd>", + " : additional parameters for mefcfg are:", + " : <filename>", + " : additional parameters for customie are:", + " : <index> <mask> <IE buffer>", + " : additional parameters for mgmtframetx are:", + " : <pkt file>", + " : additional parameter for arpfilter", + " : <filename>", + " : additional parameter for tdls_setinfo", + " : <filename>", + " : additional parameter for tdls_discovery", + " : <filename>", + " : additional parameter for tdls_setup", + " : <filename>", + " : additional parameter for tdls_teardown", + " : <filename>", + " : additional parameter for tdls_powermode", + " : <filename>", + " : additional parameter for tdls_debug <parameters>", + " : additional parameter for tdls_channel_switch", + " : <filename>", + " : additional parameter for tdls_stop_channel_switch", + " : <filename>", + " : additional parameter for tdls_cs_params", + " : <filename>", + " : additional parameter for cfgdata", + " : <type> <filename>", + " : additional parameter for sdcmd52rw", + " : <function> <reg addr.> <data>", + " : additional parameter for sdcmd53rw", + " : <func> <addr> <mode> <blksiz> <blknum> <data1> ... ...<dataN> ", + " : additional parameter for addts", + " : <filename.conf> <section# of tspec> <timeout in ms>", + " : additional parameter for delts", + " : <filename.conf> <section# of tspec>", + " : additional parameter for qconfig", + " : <[set msdu <lifetime in TUs> [Queue Id: 0-3]]", + " : [get [Queue Id: 0-3]] [def [Queue Id: 0-3]]>", + " : additional parameter for qstats", + " : <[get [User Priority: 0-7]]>", + " : additional parameter for regrdwr", + " : <type> <offset> [value]", + " : additional parameter for memrdwr", + " : <address> [value]", +}; + +t_s32 sockfd; /**< socket */ +t_s8 dev_name[IFNAMSIZ + 1]; /**< device name */ +static struct iw_priv_args *priv_args = NULL; /**< private args */ +static int we_version_compiled = 0; + /**< version compiled */ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief isdigit for String. + * + * @param x Char string + * @return MLAN_STATUS_FAILURE for non-digit. + * MLAN_STATUS_SUCCESS for digit + */ +static int +ISDIGIT(t_s8 *x) +{ + unsigned int i; + for (i = 0; i < strlen(x); i++) + if (isdigit(x[i]) == 0) + return MLAN_STATUS_FAILURE; + return MLAN_STATUS_SUCCESS; +} + +/** + * Check of decimal or hex string + * @param num string + */ +#define IS_HEX_OR_DIGIT(num) \ + (strncasecmp("0x", (num), 2)?ISDIGIT((num)):ishexstring((num))) + +/** + * @brief Get private info. + * + * @param ifname A pointer to net name + * @return MLAN_STATUS_SUCCESS--success, otherwise --fail + */ +static int +get_private_info(const t_s8 *ifname) +{ + /* This function sends the SIOCGIWPRIV command, which is + * handled by the kernel and gets the total number of + * private ioctl's available in the host driver. + */ + struct iwreq iwr; + int s, ret = MLAN_STATUS_SUCCESS; + struct iw_priv_args *ppriv = NULL; + struct iw_priv_args *new_priv; + int result = 0; + size_t size = IW_INIT_PRIV_NUM; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + return MLAN_STATUS_FAILURE; + } + + memset(&iwr, 0, sizeof(iwr)); + strncpy(iwr.ifr_name, ifname, IFNAMSIZ - 1); + + do { + /* (Re)allocate the buffer */ + new_priv = realloc(ppriv, size * sizeof(ppriv[0])); + if (new_priv == NULL) { + printf("Error: Buffer allocation failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + ppriv = new_priv; + + iwr.u.data.pointer = (caddr_t) ppriv; + iwr.u.data.length = size; + iwr.u.data.flags = 0; + + if (ioctl(s, SIOCGIWPRIV, &iwr) < 0) { + result = errno; + ret = MLAN_STATUS_FAILURE; + if (result == E2BIG) { + /* We need a bigger buffer. Check if kernel gave us any hints. */ + if (iwr.u.data.length > size) { + /* Kernel provided required size */ + size = iwr.u.data.length; + } else { + /* No hint from kernel, double the buffer size */ + size *= 2; + } + } else { + /* ioctl error */ + perror("ioctl[SIOCGIWPRIV]"); + break; + } + } else { + /* Success. Return the number of private ioctls */ + priv_args = ppriv; + ret = iwr.u.data.length; + break; + } + } while (size <= IW_MAX_PRIV_NUM); + + if ((ret == MLAN_STATUS_FAILURE) && (ppriv)) + free(ppriv); + + close(s); + + return ret; +} + +/** + * @brief Get Sub command ioctl number + * + * @param i command index + * @param priv_cnt Total number of private ioctls availabe in driver + * @param ioctl_val A pointer to return ioctl number + * @param subioctl_val A pointer to return sub-ioctl number + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int +marvell_get_subioctl_no(t_s32 i, + t_s32 priv_cnt, int *ioctl_val, int *subioctl_val) +{ + t_s32 j; + + if (priv_args[i].cmd >= SIOCDEVPRIVATE) { + *ioctl_val = priv_args[i].cmd; + *subioctl_val = 0; + return MLAN_STATUS_SUCCESS; + } + + j = -1; + + /* Find the matching *real* ioctl */ + + while ((++j < priv_cnt) + && ((priv_args[j].name[0] != '\0') || + (priv_args[j].set_args != priv_args[i].set_args) || + (priv_args[j].get_args != priv_args[i].get_args))) { + } + + /* If not found... */ + if (j == priv_cnt) { + printf("%s: Invalid private ioctl definition for: 0x%x\n", + dev_name, priv_args[i].cmd); + return MLAN_STATUS_FAILURE; + } + + /* Save ioctl numbers */ + *ioctl_val = priv_args[j].cmd; + *subioctl_val = priv_args[i].cmd; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get ioctl number + * + * @param ifname A pointer to net name + * @param priv_cmd A pointer to priv command buffer + * @param ioctl_val A pointer to return ioctl number + * @param subioctl_val A pointer to return sub-ioctl number + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int +marvell_get_ioctl_no(const t_s8 *ifname, + const t_s8 *priv_cmd, int *ioctl_val, int *subioctl_val) +{ + t_s32 i; + t_s32 priv_cnt; + int ret = MLAN_STATUS_FAILURE; + + priv_cnt = get_private_info(ifname); + + /* Are there any private ioctls? */ + if (priv_cnt <= 0 || priv_cnt > IW_MAX_PRIV_NUM) { + /* Could skip this message ? */ + printf("%-8.8s no private ioctls.\n", ifname); + } else { + for (i = 0; i < priv_cnt; i++) { + if (priv_args[i].name[0] && + !strcmp(priv_args[i].name, priv_cmd)) { + ret = marvell_get_subioctl_no(i, priv_cnt, + ioctl_val, + subioctl_val); + break; + } + } + } + + if (priv_args) { + free(priv_args); + priv_args = NULL; + } + + return ret; +} + +/** + * @brief Retrieve the ioctl and sub-ioctl numbers for the given ioctl string + * + * @param ioctl_name Private IOCTL string name + * @param ioctl_val A pointer to return ioctl number + * @param subioctl_val A pointer to return sub-ioctl number + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int +get_priv_ioctl(char *ioctl_name, int *ioctl_val, int *subioctl_val) +{ + int retval; + + retval = marvell_get_ioctl_no(dev_name, + ioctl_name, ioctl_val, subioctl_val); + + return retval; +} + +/** + * @brief Process host_cmd + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_host_cmd(int argc, char *argv[]) +{ + t_s8 cmdname[256]; + t_u8 *buf; + HostCmd_DS_GEN *hostcmd; + struct iwreq iwr; + int ret = MLAN_STATUS_SUCCESS; + int ioctl_val, subioctl_val; + FILE *fp = NULL; + + if (get_priv_ioctl("hostcmd", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + if (argc < 5) { + printf("Error: invalid no of arguments\n"); + printf("Syntax: ./mlanconfig mlanX hostcmd <hostcmd.conf> <cmdname>\n"); + exit(1); + } + + if ((fp = fopen(argv[3], "r")) == NULL) { + fprintf(stderr, "Cannot open file %s\n", argv[3]); + exit(1); + } + + buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (buf == NULL) { + printf("Error: allocate memory for hostcmd failed\n"); + fclose(fp); + return -ENOMEM; + } + snprintf(cmdname, sizeof(cmdname), "%s", argv[4]); + ret = prepare_host_cmd_buffer(fp, cmdname, buf); + fclose(fp); + + if (ret == MLAN_STATUS_FAILURE) + goto _exit_; + + hostcmd = (HostCmd_DS_GEN *)buf; + memset(&iwr, 0, sizeof(iwr)); + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = (t_u8 *)hostcmd; + iwr.u.data.length = le16_to_cpu(hostcmd->size); + + iwr.u.data.flags = 0; + if (ioctl(sockfd, ioctl_val, &iwr)) { + fprintf(stderr, + "mlanconfig: MLANHOSTCMD is not supported by %s\n", + dev_name); + ret = MLAN_STATUS_FAILURE; + goto _exit_; + } + ret = process_host_cmd_resp(buf); + +_exit_: + if (buf) + free(buf); + + return ret; +} + +/** + * @brief get range + * + * @return MLAN_STATUS_SUCCESS--success, otherwise --fail + */ +static int +get_range(t_void) +{ + struct iw_range *range; + struct iwreq iwr; + size_t buf_len; + + buf_len = sizeof(struct iw_range) + 500; + range = malloc(buf_len); + if (range == NULL) { + printf("Error: allocate memory for iw_range failed\n"); + return -ENOMEM; + } + memset(range, 0, buf_len); + memset(&iwr, 0, sizeof(struct iwreq)); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buf_len; + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + + if ((ioctl(sockfd, SIOCGIWRANGE, &iwr)) < 0) { + printf("Get Range Results Failed\n"); + free(range); + return MLAN_STATUS_FAILURE; + } + we_version_compiled = range->we_version_compiled; + printf("Driver build with Wireless Extension %d\n", + range->we_version_compiled); + free(range); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Display usage + * + * @return NA + */ +static t_void +display_usage(t_void) +{ + t_u32 i; + for (i = 0; i < NELEMENTS(usage); i++) + fprintf(stderr, "%s\n", usage[i]); +} + +/** + * @brief Find command + * + * @param maxcmds max command number + * @param cmds A pointer to commands buffer + * @param cmd A pointer to command buffer + * @return index of command or MLAN_STATUS_FAILURE + */ +static int +findcommand(t_s32 maxcmds, t_s8 *cmds[], t_s8 *cmd) +{ + t_s32 i; + + for (i = 0; i < maxcmds; i++) { + if (!strcasecmp(cmds[i], cmd)) { + return i; + } + } + + return MLAN_STATUS_FAILURE; +} + +/** + * @brief Process arpfilter + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_arpfilter(int argc, char *argv[]) +{ + t_u8 *buf; + struct iwreq iwr; + t_u16 length = 0; + int ret = MLAN_STATUS_SUCCESS; + FILE *fp = NULL; + int ioctl_val, subioctl_val; + + if (get_priv_ioctl("arpfilter", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + if (argc < 4) { + printf("Error: invalid no of arguments\n"); + printf("Syntax: ./mlanconfig mlanX arpfilter <arpfilter.conf>\n"); + exit(1); + } + + if ((fp = fopen(argv[3], "r")) == NULL) { + fprintf(stderr, "Cannot open file %s\n", argv[3]); + return MLAN_STATUS_FAILURE; + } + buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (buf == NULL) { + printf("Error: allocate memory for arpfilter failed\n"); + fclose(fp); + return -ENOMEM; + } + ret = prepare_arp_filter_buffer(fp, buf, &length); + fclose(fp); + + if (ret == MLAN_STATUS_FAILURE) + goto _exit_; + + memset(&iwr, 0, sizeof(iwr)); + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = buf; + iwr.u.data.length = length; + iwr.u.data.flags = 0; + if (ioctl(sockfd, ioctl_val, &iwr)) { + fprintf(stderr, + "mlanconfig: arpfilter command is not supported by %s\n", + dev_name); + ret = MLAN_STATUS_FAILURE; + goto _exit_; + } + +_exit_: + if (buf) + free(buf); + + return ret; +} + +/** + * @brief Process cfgdata + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_cfg_data(int argc, char *argv[]) +{ + t_u8 *buf; + HostCmd_DS_GEN *hostcmd; + struct iwreq iwr; + int ret = MLAN_STATUS_SUCCESS; + int ioctl_val, subioctl_val; + FILE *fp = NULL; + + if (get_priv_ioctl("hostcmd", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + if (argc < 4 || argc > 5) { + printf("Error: invalid no of arguments\n"); + printf("Syntax: ./mlanconfig mlanX cfgdata <register type> <filename>\n"); + exit(1); + } + + if (argc == 5) { + if ((fp = fopen(argv[4], "r")) == NULL) { + fprintf(stderr, "Cannot open file %s\n", argv[3]); + exit(1); + } + } + buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (buf == NULL) { + printf("Error: allocate memory for hostcmd failed\n"); + if (argc == 5) + fclose(fp); + return -ENOMEM; + } + ret = prepare_cfg_data_buffer(argc, argv, fp, buf); + if (argc == 5) + fclose(fp); + + if (ret == MLAN_STATUS_FAILURE) + goto _exit_; + + hostcmd = (HostCmd_DS_GEN *)buf; + memset(&iwr, 0, sizeof(iwr)); + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = (t_u8 *)hostcmd; + iwr.u.data.length = le16_to_cpu(hostcmd->size); + + iwr.u.data.flags = 0; + if (ioctl(sockfd, ioctl_val, &iwr)) { + fprintf(stderr, + "mlanconfig: MLANHOSTCMD is not supported by %s\n", + dev_name); + ret = MLAN_STATUS_FAILURE; + goto _exit_; + } + ret = process_host_cmd_resp(buf); + +_exit_: + if (buf) + free(buf); + + return ret; +} + +/** + * @brief read current command + * @param ptr A pointer to data + * @param curCmd A pointer to the buf which will hold current command + * @return NULL or the pointer to the left command buf + */ +static t_s8 * +readCurCmd(t_s8 *ptr, t_s8 *curCmd) +{ + t_s32 i = 0; +#define MAX_CMD_SIZE 64 /**< Max command size */ + + while (*ptr != ']' && i < (MAX_CMD_SIZE - 1)) + curCmd[i++] = *(++ptr); + + if (*ptr != ']') + return NULL; + + curCmd[i - 1] = '\0'; + + return ++ptr; +} + +/** + * @brief parse command and hex data + * @param fp A pointer to FILE stream + * @param dst A pointer to the dest buf + * @param cmd A pointer to command buf for search + * @return Length of hex data or MLAN_STATUS_FAILURE + */ +static int +fparse_for_cmd_and_hex(FILE * fp, t_u8 *dst, t_u8 *cmd) +{ + t_s8 *ptr; + t_u8 *dptr; + t_s8 buf[256], curCmd[64]; + t_s32 isCurCmd = 0; + + dptr = dst; + while (fgets(buf, sizeof(buf), fp)) { + ptr = buf; + + while (*ptr) { + /* skip leading spaces */ + while (*ptr && isspace(*ptr)) + ptr++; + + /* skip blank lines and lines beginning with '#' */ + if (*ptr == '\0' || *ptr == '#') + break; + + if (*ptr == '[' && *(ptr + 1) != '/') { + if (!(ptr = readCurCmd(ptr, curCmd))) + return MLAN_STATUS_FAILURE; + + if (strcasecmp(curCmd, (char *)cmd)) /* Not equal */ + isCurCmd = 0; + else + isCurCmd = 1; + } + + /* Ignore the rest if it is not correct cmd */ + if (!isCurCmd) + break; + + if (*ptr == '[' && *(ptr + 1) == '/') + return (dptr - dst); + + if (isxdigit(*ptr)) { + ptr = convert2hex(ptr, dptr++); + } else { + /* Invalid character on data line */ + ptr++; + } + } + } + + return MLAN_STATUS_FAILURE; +} + +/** + * @brief Send an ADDTS command to the associated AP + * + * Process a given conf file for a specific TSPEC data block. Send the + * TSPEC along with any other IEs to the driver/firmware for transmission + * in an ADDTS request to the associated AP. + * + * Return the execution status of the command as well as the ADDTS response + * from the AP if any. + * + * mlanconfig mlanX addts <filename.conf> <section# of tspec> <timeout in ms> + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_addts(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + wlan_ioctl_wmm_addts_req_t addtsReq; + + FILE *fp = NULL; + char filename[48]; + char config_id[20]; + + memset(&addtsReq, 0x00, sizeof(addtsReq)); + memset(filename, 0x00, sizeof(filename)); + + if (get_priv_ioctl("addts", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + if (argc != 6) { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + + strncpy(filename, argv[3], MIN(sizeof(filename) - 1, strlen(argv[3]))); + if ((fp = fopen(filename, "r")) == NULL) { + perror("fopen"); + fprintf(stderr, "Cannot open file %s\n", argv[3]); + return -EFAULT;; + } + + snprintf(config_id, sizeof(config_id), "tspec%d", atoi(argv[4])); + + addtsReq.ieDataLen = fparse_for_cmd_and_hex(fp, + addtsReq.ieData, + (t_u8 *)config_id); + + if (addtsReq.ieDataLen > 0) { + printf("Found %d bytes in the %s section of conf file %s\n", + (int)addtsReq.ieDataLen, config_id, filename); + } else { + fprintf(stderr, "section %s not found in %s\n", + config_id, filename); + if (fp) + fclose(fp); + return -EFAULT; + } + + addtsReq.timeout_ms = atoi(argv[5]); + + printf("Cmd Input:\n"); + hexdump(config_id, addtsReq.ieData, addtsReq.ieDataLen, ' '); + + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.flags = subioctl_val; + iwr.u.data.pointer = (caddr_t) & addtsReq; + iwr.u.data.length = (sizeof(addtsReq) + - sizeof(addtsReq.ieData) + + addtsReq.ieDataLen); + + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("mlanconfig: addts ioctl"); + if (fp) + fclose(fp); + return -EFAULT; + } + + printf("Cmd Output:\n"); + printf("ADDTS Command Result = %d\n", addtsReq.commandResult); + printf("ADDTS IEEE Status = %d\n", addtsReq.ieeeStatusCode); + hexdump(config_id, addtsReq.ieData, addtsReq.ieDataLen, ' '); + + if (fp) + fclose(fp); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Send a DELTS command to the associated AP + * + * Process a given conf file for a specific TSPEC data block. Send the + * TSPEC along with any other IEs to the driver/firmware for transmission + * in a DELTS request to the associated AP. + * + * Return the execution status of the command. There is no response to a + * DELTS from the AP. + * + * mlanconfig mlanX delts <filename.conf> <section# of tspec> + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_delts(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + wlan_ioctl_wmm_delts_req_t deltsReq; + + FILE *fp = NULL; + char filename[48]; + char config_id[20]; + + memset(&deltsReq, 0x00, sizeof(deltsReq)); + memset(filename, 0x00, sizeof(filename)); + + if (get_priv_ioctl("delts", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + if (argc != 5) { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + + strncpy(filename, argv[3], MIN(sizeof(filename) - 1, strlen(argv[3]))); + if ((fp = fopen(filename, "r")) == NULL) { + perror("fopen"); + fprintf(stderr, "Cannot open file %s\n", argv[3]); + return -EFAULT; + } + + snprintf(config_id, sizeof(config_id), "tspec%d", atoi(argv[4])); + + deltsReq.ieDataLen = fparse_for_cmd_and_hex(fp, + deltsReq.ieData, + (t_u8 *)config_id); + + if (deltsReq.ieDataLen > 0) { + printf("Found %d bytes in the %s section of conf file %s\n", + (int)deltsReq.ieDataLen, config_id, filename); + } else { + fprintf(stderr, "section %s not found in %s\n", + config_id, filename); + if (fp) + fclose(fp); + return -EFAULT; + } + + printf("Cmd Input:\n"); + hexdump(config_id, deltsReq.ieData, deltsReq.ieDataLen, ' '); + + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.flags = subioctl_val; + iwr.u.data.pointer = (caddr_t) & deltsReq; + iwr.u.data.length = (sizeof(deltsReq) + - sizeof(deltsReq.ieData) + + deltsReq.ieDataLen); + + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("mlanconfig: delts ioctl"); + if (fp) + fclose(fp); + return -EFAULT; + } + + printf("Cmd Output:\n"); + printf("DELTS Command Result = %d\n", deltsReq.commandResult); + if (fp) + fclose(fp); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Send a WMM AC Queue configuration command to get/set/default params + * + * Configure or get the parameters of a WMM AC queue. The command takes + * an optional Queue Id as a last parameter. Without the queue id, all + * queues will be acted upon. + * + * mlanconfig mlanX qconfig set msdu <lifetime in TUs> [Queue Id: 0-3] + * mlanconfig mlanX qconfig get [Queue Id: 0-3] + * mlanconfig mlanX qconfig def [Queue Id: 0-3] + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_qconfig(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + wlan_ioctl_wmm_queue_config_t queue_config_cmd; + mlan_wmm_ac_e ac_idx; + mlan_wmm_ac_e ac_idx_start; + mlan_wmm_ac_e ac_idx_stop; + + const char *ac_str_tbl[] = { "BK", "BE", "VI", "VO" }; + + if (argc < 4) { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + + if (get_priv_ioctl("qconfig", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + memset(&queue_config_cmd, 0x00, sizeof(queue_config_cmd)); + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = (caddr_t) & queue_config_cmd; + iwr.u.data.length = sizeof(queue_config_cmd); + iwr.u.data.flags = subioctl_val; + + if (strcmp(argv[3], "get") == 0) { + /* 3 4 5 */ + /* qconfig get [qid] */ + if (argc == 4) { + ac_idx_start = WMM_AC_BK; + ac_idx_stop = WMM_AC_VO; + } else if (argc == 5) { + if (atoi(argv[4]) < WMM_AC_BK || + atoi(argv[4]) > WMM_AC_VO) { + fprintf(stderr, "ERROR: Invalid Queue ID!\n"); + return -EINVAL; + } + ac_idx_start = atoi(argv[4]); + ac_idx_stop = ac_idx_start; + } else { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + queue_config_cmd.action = WMM_QUEUE_CONFIG_ACTION_GET; + + for (ac_idx = ac_idx_start; ac_idx <= ac_idx_stop; ac_idx++) { + queue_config_cmd.accessCategory = ac_idx; + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("qconfig ioctl"); + } else { + printf("qconfig %s(%d): MSDU Lifetime GET = 0x%04x (%d)\n", ac_str_tbl[ac_idx], ac_idx, queue_config_cmd.msduLifetimeExpiry, queue_config_cmd.msduLifetimeExpiry); + } + } + } else if (strcmp(argv[3], "set") == 0) { + if ((argc >= 5) && strcmp(argv[4], "msdu") == 0) { + /* 3 4 5 6 7 */ + /* qconfig set msdu <value> [qid] */ + if (argc == 6) { + ac_idx_start = WMM_AC_BK; + ac_idx_stop = WMM_AC_VO; + } else if (argc == 7) { + if (atoi(argv[6]) < WMM_AC_BK || + atoi(argv[6]) > WMM_AC_VO) { + fprintf(stderr, + "ERROR: Invalid Queue ID!\n"); + return -EINVAL; + } + ac_idx_start = atoi(argv[6]); + ac_idx_stop = ac_idx_start; + } else { + fprintf(stderr, + "Invalid number of parameters!\n"); + return -EINVAL; + } + queue_config_cmd.action = WMM_QUEUE_CONFIG_ACTION_SET; + queue_config_cmd.msduLifetimeExpiry = atoi(argv[5]); + + for (ac_idx = ac_idx_start; ac_idx <= ac_idx_stop; + ac_idx++) { + queue_config_cmd.accessCategory = ac_idx; + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("qconfig ioctl"); + } else { + printf("qconfig %s(%d): MSDU Lifetime SET = 0x%04x (%d)\n", ac_str_tbl[ac_idx], ac_idx, queue_config_cmd.msduLifetimeExpiry, queue_config_cmd.msduLifetimeExpiry); + } + } + } else { + /* Only MSDU Lifetime provisioning accepted for now */ + fprintf(stderr, "Invalid set parameter: s/b [msdu]\n"); + return -EINVAL; + } + } else if (strncmp(argv[3], "def", strlen("def")) == 0) { + /* 3 4 5 */ + /* qconfig def [qid] */ + if (argc == 4) { + ac_idx_start = WMM_AC_BK; + ac_idx_stop = WMM_AC_VO; + } else if (argc == 5) { + if (atoi(argv[4]) < WMM_AC_BK || + atoi(argv[4]) > WMM_AC_VO) { + fprintf(stderr, "ERROR: Invalid Queue ID!\n"); + return -EINVAL; + } + ac_idx_start = atoi(argv[4]); + ac_idx_stop = ac_idx_start; + } else { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + queue_config_cmd.action = WMM_QUEUE_CONFIG_ACTION_DEFAULT; + + for (ac_idx = ac_idx_start; ac_idx <= ac_idx_stop; ac_idx++) { + queue_config_cmd.accessCategory = ac_idx; + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("qconfig ioctl"); + } else { + printf("qconfig %s(%d): MSDU Lifetime DEFAULT = 0x%04x (%d)\n", ac_str_tbl[ac_idx], ac_idx, queue_config_cmd.msduLifetimeExpiry, queue_config_cmd.msduLifetimeExpiry); + } + } + } else { + fprintf(stderr, + "Invalid qconfig command; s/b [set, get, default]\n"); + return -EINVAL; + } + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Turn on/off or retrieve and clear the queue statistics for a UP + * + * Turn the statistics collection on/off for a given UP or retrieve the + * current accumulated stats and clear them from the firmware. The command + * takes an optional Queue Id as a last parameter. Without the queue id, + * all queues will be acted upon. + * + * mlanconfig mlanX qstats get [User Priority: 0-7] + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_qstats(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + wlan_ioctl_wmm_queue_stats_t queue_stats_cmd; + t_u8 up_idx; + t_u8 up_idx_start; + t_u8 up_idx_stop; + t_u16 usedTime[MAX_USER_PRIORITIES]; + t_u16 policedTime[MAX_USER_PRIORITIES]; + + const char *ac_str_tbl[] = { "BE", "BK", "BK", "BE", + "VI", "VI", "VO", "VO" + }; + + if (argc < 3) { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + + if (get_priv_ioctl("qstats", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + printf("\n"); + + memset(usedTime, 0x00, sizeof(usedTime)); + memset(policedTime, 0x00, sizeof(policedTime)); + memset(&queue_stats_cmd, 0x00, sizeof(queue_stats_cmd)); + + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = (caddr_t) & queue_stats_cmd; + iwr.u.data.length = sizeof(queue_stats_cmd); + iwr.u.data.flags = subioctl_val; + + if ((argc > 3) && strcmp(argv[3], "on") == 0) { + if (argc == 4) { + up_idx_start = 0; + up_idx_stop = 7; + } else if (argc == 5) { + if (atoi(argv[4]) < 0 || atoi(argv[4]) > 7) { + fprintf(stderr, + "ERROR: Invalid User Priority!\n"); + return -EINVAL; + } + up_idx_start = atoi(argv[4]); + up_idx_stop = up_idx_start; + } else { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + queue_stats_cmd.action = WMM_STATS_ACTION_START; + for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) { + queue_stats_cmd.userPriority = up_idx; + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("qstats ioctl"); + } else { + printf("qstats UP%d, %s turned on\n", + up_idx, ac_str_tbl[up_idx]); + } + } + } else if ((argc > 3) && strcmp(argv[3], "off") == 0) { + if (argc == 4) { + up_idx_start = 0; + up_idx_stop = 7; + } else if (argc == 5) { + if (atoi(argv[4]) < 0 || atoi(argv[4]) > 7) { + fprintf(stderr, + "ERROR: Invalid User Priority!\n"); + return -EINVAL; + } + up_idx_start = atoi(argv[4]); + up_idx_stop = up_idx_start; + } else { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + queue_stats_cmd.action = WMM_STATS_ACTION_STOP; + for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) { + queue_stats_cmd.userPriority = up_idx; + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("qstats ioctl"); + } else { + printf("qstats UP%d, %s turned off\n", + up_idx, ac_str_tbl[up_idx]); + } + } + } else if ((argc >= 3) && + ((argc == 3) ? 1 : (strcmp(argv[3], "get") == 0))) { + /* If the user types: "mlanconfig mlanX qstats" without get argument. + * The mlanconfig application invokes "get" option for all UPs + */ + if ((argc == 4) || (argc == 3)) { + up_idx_start = 0; + up_idx_stop = 7; + } else if (argc == 5) { + if (atoi(argv[4]) < 0 || atoi(argv[4]) > 7) { + fprintf(stderr, + "ERROR: Invalid User Priority!\n"); + return -EINVAL; + } + up_idx_start = atoi(argv[4]); + up_idx_stop = up_idx_start; + } else { + fprintf(stderr, "Invalid number of parameters!\n"); + return -EINVAL; + } + printf("%s %6s %5s %8s %8s %6s %6s %6s %6s %6s %6s\n", + "AC-UP", "Count", "Loss", "TxDly", "QDly", + "<=5", "<=10", "<=20", "<=30", "<=50", ">50"); + printf("----------------------------------" + "---------------------------------------------\n"); + queue_stats_cmd.action = WMM_STATS_ACTION_GET_CLR; + + for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) { + queue_stats_cmd.userPriority = up_idx; + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("qstats ioctl"); + } else { + printf(" %s-%d %6u %5u %8u %8u %6u %6u %6u %6u %6u %6u\n", ac_str_tbl[up_idx], up_idx, queue_stats_cmd.pktCount, queue_stats_cmd.pktLoss, (unsigned int)queue_stats_cmd.avgTxDelay, (unsigned int)queue_stats_cmd.avgQueueDelay, queue_stats_cmd.delayHistogram[0], queue_stats_cmd.delayHistogram[1], queue_stats_cmd.delayHistogram[2], queue_stats_cmd.delayHistogram[3], (queue_stats_cmd.delayHistogram[4] + queue_stats_cmd.delayHistogram[5]), queue_stats_cmd.delayHistogram[6]); + + usedTime[up_idx] = queue_stats_cmd.usedTime; + policedTime[up_idx] = + queue_stats_cmd.policedTime; + } + } + + printf("----------------------------------" + "---------------------------------------------\n"); + printf("\nAC-UP UsedTime PolicedTime\n"); + printf("------------------------------------\n"); + + for (up_idx = up_idx_start; up_idx <= up_idx_stop; up_idx++) { + printf(" %s-%d %6u %6u\n", + ac_str_tbl[up_idx], + up_idx, + (unsigned int)usedTime[up_idx], + (unsigned int)policedTime[up_idx]); + } + } else { + fprintf(stderr, "Invalid qstats command;\n"); + return -EINVAL; + } + printf("\n"); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get the current status of the WMM Queues + * + * Command: mlanconfig mlanX qstatus + * + * Retrieve the following information for each AC if wmm is enabled: + * - WMM IE ACM Required + * - Firmware Flow Required + * - Firmware Flow Established + * - Firmware Queue Enabled + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_wmm_qstatus(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + wlan_ioctl_wmm_queue_status_t qstatus; + mlan_wmm_ac_e acVal; + + if (get_priv_ioctl("qstatus", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + memset(&qstatus, 0x00, sizeof(qstatus)); + + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.flags = subioctl_val; + iwr.u.data.pointer = (caddr_t) & qstatus; + iwr.u.data.length = (sizeof(qstatus)); + + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("mlanconfig: qstatus ioctl"); + return -EFAULT; + } + + for (acVal = WMM_AC_BK; acVal <= WMM_AC_VO; acVal++) { + switch (acVal) { + case WMM_AC_BK: + printf("BK: "); + break; + case WMM_AC_BE: + printf("BE: "); + break; + case WMM_AC_VI: + printf("VI: "); + break; + case WMM_AC_VO: + printf("VO: "); + break; + default: + printf("??: "); + } + + printf("ACM[%c], FlowReq[%c], FlowCreated[%c], Enabled[%c]," + " DE[%c], TE[%c]\n", + (qstatus.acStatus[acVal].wmmAcm ? 'X' : ' '), + (qstatus.acStatus[acVal].flowRequired ? 'X' : ' '), + (qstatus.acStatus[acVal].flowCreated ? 'X' : ' '), + (qstatus.acStatus[acVal].disabled ? ' ' : 'X'), + (qstatus.acStatus[acVal].deliveryEnabled ? 'X' : ' '), + (qstatus.acStatus[acVal].triggerEnabled ? 'X' : ' ')); + } + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get the current status of the WMM Traffic Streams + * + * Command: mlanconfig mlanX ts_status + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_wmm_ts_status(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + wlan_ioctl_wmm_ts_status_t ts_status; + int tid; + + const char *ac_str_tbl[] = { "BK", "BE", "VI", "VO" }; + + if (get_priv_ioctl("ts_status", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + printf("\nTID Valid AC UP PSB FlowDir MediumTime\n"); + printf("---------------------------------------------------\n"); + + for (tid = 0; tid <= 7; tid++) { + memset(&ts_status, 0x00, sizeof(ts_status)); + ts_status.tid = tid; + + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.flags = subioctl_val; + iwr.u.data.pointer = (caddr_t) & ts_status; + iwr.u.data.length = (sizeof(ts_status)); + + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("mlanconfig: ts_status ioctl"); + return -EFAULT; + } + + printf(" %02d %3s %2s %u %c ", + ts_status.tid, + (ts_status.valid ? "Yes" : "No"), + (ts_status. + valid ? ac_str_tbl[ts_status.accessCategory] : "--"), + ts_status.userPriority, (ts_status.psb ? 'U' : 'L')); + + if ((ts_status.flowDir & 0x03) == 0) { + printf("%s", " ---- "); + } else { + printf("%2s%4s", + (ts_status.flowDir & 0x01 ? "Up" : ""), + (ts_status.flowDir & 0x02 ? "Down" : "")); + } + + printf("%12u\n", ts_status.mediumTime); + } + + printf("\n"); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Provides interface to perform read/write operations on regsiter + * + * Command: mlanconfig mlanX regrdwr <type> <offset> [value] + * + * @param argc Number of arguments + * @param argv Pointer to the arguments + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_regrdwr(int argc, char *argv[]) +{ + struct iwreq iwr; + int ioctl_val, subioctl_val; + t_u32 type, offset, value; + t_u8 buf[100]; + HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf; + int ret = MLAN_STATUS_SUCCESS; + + /* Check arguments */ + if ((argc < 5) || (argc > 6)) { + printf("Parameters for regrdwr: <type> <offset> [value]\n"); + return -EINVAL; + } + + if (get_priv_ioctl("hostcmd", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + type = a2hex_or_atoi(argv[3]); + offset = a2hex_or_atoi(argv[4]); + if (argc > 5) + value = a2hex_or_atoi(argv[5]); + if ((ret = prepare_hostcmd_regrdwr(type, offset, + (argc > 5) ? &value : NULL, buf))) { + return ret; + } + + memset(&iwr, 0, sizeof(iwr)); + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = buf; + iwr.u.data.length = le16_to_cpu(hostcmd->size); + iwr.u.data.flags = 0; + + if (ioctl(sockfd, ioctl_val, &iwr)) { + fprintf(stderr, + "mlanconfig: MLANHOSTCMD is not supported by %s\n", + dev_name); + return MLAN_STATUS_FAILURE; + } + ret = process_host_cmd_resp(buf); + + return ret; +} + +/** + * @brief Provides interface to perform read/write the adapter memory + * + * Command: mlanconfig mlanX memrdwr <address> [value] + * + * @param argc Number of arguments + * @param argv Pointer to the arguments + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_memrdwr(int argc, char *argv[]) +{ + struct iwreq iwr; + int ioctl_val, subioctl_val; + t_u32 address, value; + t_u8 buf[100] = { 0 }; + HostCmd_DS_MEM *pmem; + HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf; + int ret = MLAN_STATUS_SUCCESS; + + pmem = (HostCmd_DS_MEM *)(buf + S_DS_GEN); + + /* Check arguments */ + if ((argc < 4) || (argc > 5)) { + printf("Parameters for memrdwr: <address> [value]\n"); + return -EINVAL; + } + + if (get_priv_ioctl("hostcmd", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + address = a2hex_or_atoi(argv[3]); + pmem->addr = cpu_to_le32(address); + if (argc > 4) { + pmem->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + value = a2hex_or_atoi(argv[4]); + pmem->value = cpu_to_le32(value); + } else { + pmem->action = cpu_to_le16(HostCmd_ACT_GEN_GET); + pmem->value = 0; + } + hostcmd->command = cpu_to_le16(HostCmd_CMD_MEM_ACCESS); + hostcmd->size = cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_MEM)); + hostcmd->seq_num = 0; + hostcmd->result = 0; + + memset(&iwr, 0, sizeof(iwr)); + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = buf; + iwr.u.data.length = le16_to_cpu(hostcmd->size); + iwr.u.data.flags = 0; + + if (ioctl(sockfd, ioctl_val, &iwr)) { + fprintf(stderr, + "mlanconfig: MLANHOSTCMD is not supported by %s\n", + dev_name); + return MLAN_STATUS_FAILURE; + } + ret = process_host_cmd_resp(buf); + + return ret; +} + +/** custom IE, auto mask value */ +#define CUSTOM_IE_AUTO_MASK 0xffff + +/** + * @brief Show usage information for the customie + * command + * + * $return N/A + **/ +void +print_custom_ie_usage(void) +{ + printf("\nUsage : customie [INDEX] [MASK] [IEBuffer]"); + printf("\n empty - Get all IE settings\n"); + printf("\n INDEX: 0 - Get/Set IE index 0 setting"); + printf("\n 1 - Get/Set IE index 1 setting"); + printf("\n 2 - Get/Set IE index 2 setting"); + printf("\n 3 - Get/Set IE index 3 setting"); + printf("\n . "); + printf("\n . "); + printf("\n . "); + printf("\n -1 - Append/Delete IE automatically"); + printf("\n Delete will delete the IE from the matching IE buffer"); + printf("\n Append will append the IE to the buffer with the same mask"); + printf("\n MASK : Management subtype mask value as per bit defintions"); + printf("\n : Bit 0 - Association request."); + printf("\n : Bit 1 - Association response."); + printf("\n : Bit 2 - Reassociation request."); + printf("\n : Bit 3 - Reassociation response."); + printf("\n : Bit 4 - Probe request."); + printf("\n : Bit 5 - Probe response."); + printf("\n : Bit 8 - Beacon."); + printf("\n MASK : MASK = 0 to clear the mask and the IE buffer"); + printf("\n IEBuffer : IE Buffer in hex (max 256 bytes)\n\n"); + return; +} + +/** + * @brief Converts a string to hex value + * + * @param str A pointer to the string + * @param raw A pointer to the raw data buffer + * @return Number of bytes read + **/ +int +string2raw(char *str, unsigned char *raw) +{ + int len = (strlen(str) + 1) / 2; + + do { + if (!isxdigit(*str)) { + return -1; + } + *str = toupper(*str); + *raw = CHAR2INT(*str) << 4; + ++str; + *str = toupper(*str); + if (*str == '\0') + break; + *raw |= CHAR2INT(*str); + ++raw; + } while (*++str != '\0'); + return len; +} + +/** + * @brief Creates a hostcmd request for custom IE settings + * and sends to the driver + * + * Usage: "customie [INDEX] [MASK] [IEBuffer]" + * + * Options: INDEX : 0 - Get/Delete IE index 0 setting + * 1 - Get/Delete IE index 1 setting + * 2 - Get/Delete IE index 2 setting + * 3 - Get/Delete IE index 3 setting + * . + * . + * . + * -1 - Append IE at the IE buffer with same MASK + * MASK : Management subtype mask value + * IEBuffer: IE Buffer in hex + * empty - Get all IE settings + * + * @param argc Number of arguments + * @param argv Pointer to the arguments + * @return N/A + **/ +static int +process_custom_ie(int argc, char *argv[]) +{ + tlvbuf_custom_ie *tlv = NULL; + tlvbuf_max_mgmt_ie *max_mgmt_ie_tlv = NULL; + custom_ie *ie_ptr = NULL; + t_u8 *buffer = NULL; + t_u16 buf_len = 0; + t_u16 mgmt_subtype_mask = 0; + int ie_buf_len = 0, ie_len = 0, i = 0; + struct ifreq ifr; + + /* mlanconfig mlan0 customie idx flag buf */ + if (argc > 6) { + printf("ERR:Too many arguments.\n"); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + /* Error checks and initialize the command length */ + if (argc > 3) { + if (((IS_HEX_OR_DIGIT(argv[3]) == MLAN_STATUS_FAILURE) && + (atoi(argv[3]) != -1)) || (atoi(argv[3]) < -1)) { + printf("ERR:Illegal index %s\n", argv[3]); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + } + switch (argc) { + case 3: + buf_len = MRVDRV_SIZE_OF_CMD_BUFFER; + break; + case 4: + if (atoi(argv[3]) < 0) { + printf("ERR:Illegal index %s. Must be either greater than or equal to 0 for Get Operation \n", argv[3]); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + buf_len = MRVDRV_SIZE_OF_CMD_BUFFER; + break; + case 5: + if (MLAN_STATUS_FAILURE == ishexstring(argv[4]) || + A2HEXDECIMAL(argv[4]) != 0) { + printf("ERR: Mask value should be 0 to clear IEBuffers.\n"); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + if (atoi(argv[3]) == -1) { + printf("ERR: You must provide buffer for automatic deletion.\n"); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + buf_len = sizeof(tlvbuf_custom_ie) + sizeof(custom_ie); + break; + case 6: + /* This is to check negative numbers and special symbols */ + if (MLAN_STATUS_FAILURE == IS_HEX_OR_DIGIT(argv[4])) { + printf("ERR:Mask value must be 0 or hex digits\n"); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + /* If above check is passed and mask is not hex, then it must be 0 */ + if ((ISDIGIT(argv[4]) == MLAN_STATUS_SUCCESS) && atoi(argv[4])) { + printf("ERR:Mask value must be 0 or hex digits\n "); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + if (MLAN_STATUS_FAILURE == ishexstring(argv[5])) { + printf("ERR:Only hex digits are allowed\n"); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + ie_buf_len = strlen(argv[5]); + if (!strncasecmp("0x", argv[5], 2)) { + ie_len = (ie_buf_len - 2 + 1) / 2; + argv[5] += 2; + } else + ie_len = (ie_buf_len + 1) / 2; + if (ie_len > MAX_IE_BUFFER_LEN) { + printf("ERR:Incorrect IE length %d\n", ie_buf_len); + print_custom_ie_usage(); + return MLAN_STATUS_FAILURE; + } + mgmt_subtype_mask = (t_u16)A2HEXDECIMAL(argv[4]); + buf_len = sizeof(tlvbuf_custom_ie) + sizeof(custom_ie) + ie_len; + break; + } + /* Initialize the command buffer */ + buffer = (t_u8 *)malloc(buf_len); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + return MLAN_STATUS_FAILURE; + } + memset(buffer, 0, buf_len); + tlv = (tlvbuf_custom_ie *)buffer; + tlv->tag = MRVL_MGMT_IE_LIST_TLV_ID; + if (argc == 3 || argc == 4) { + if (argc == 3) + tlv->length = 0; + else { + tlv->length = sizeof(t_u16); + ie_ptr = (custom_ie *)(tlv->ie_data); + ie_ptr->ie_index = (t_u16)(atoi(argv[3])); + } + } else { + /* Locate headers */ + ie_ptr = (custom_ie *)(tlv->ie_data); + /* Set TLV fields */ + tlv->length = sizeof(custom_ie) + ie_len; + ie_ptr->ie_index = atoi(argv[3]); + ie_ptr->mgmt_subtype_mask = mgmt_subtype_mask; + ie_ptr->ie_length = ie_len; + if (argc == 6) + string2raw(argv[5], ie_ptr->ie_buffer); + } + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)buffer; + /* Perform ioctl */ + if (ioctl(sockfd, CUSTOM_IE_CFG, &ifr)) { + perror("ioctl[CUSTOM_IE_CFG]"); + printf("ERR:Command sending failed!\n"); + if (buffer) + free(buffer); + return MLAN_STATUS_FAILURE; + } + /* Print response */ + if (argc > 4) { + printf("custom IE setting successful\n"); + } else { + printf("Querying custom IE successful\n"); + tlv = (tlvbuf_custom_ie *)buffer; + ie_len = tlv->length; + ie_ptr = (custom_ie *)(tlv->ie_data); + while ((unsigned int)ie_len >= sizeof(custom_ie)) { + printf("Index [%d]\n", ie_ptr->ie_index); + if (ie_ptr->ie_length) + printf("Management Subtype Mask = 0x%02x\n", + (ie_ptr->mgmt_subtype_mask) == 0 ? + CUSTOM_IE_AUTO_MASK : + (ie_ptr->mgmt_subtype_mask)); + else + printf("Management Subtype Mask = 0x%02x\n", + (ie_ptr->mgmt_subtype_mask)); + hexdump("IE Buffer", (void *)ie_ptr->ie_buffer, + ie_ptr->ie_length, ' '); + ie_len -= sizeof(custom_ie) + ie_ptr->ie_length; + ie_ptr = (custom_ie *)((t_u8 *)ie_ptr + + sizeof(custom_ie) + + ie_ptr->ie_length); + } + } + max_mgmt_ie_tlv = + (tlvbuf_max_mgmt_ie *)(buffer + sizeof(tlvbuf_custom_ie) + + tlv->length); + if (max_mgmt_ie_tlv) { + if (max_mgmt_ie_tlv->tag == MRVL_MAX_MGMT_IE_TLV_ID) { + for (i = 0; i < max_mgmt_ie_tlv->count; i++) { + printf("buf%d_size = %d\n", i, + max_mgmt_ie_tlv->info[i].buf_size); + printf("number of buffers = %d\n", + max_mgmt_ie_tlv->info[i].buf_count); + printf("\n"); + } + } + } + if (buffer) + free(buffer); + + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Get one line from the File + * + * @param fp File handler + * @param str Storage location for data. + * @param size Maximum number of characters to read. + * @param lineno A pointer to return current line number + * @return returns string or NULL + */ +char * +mlan_config_get_line(FILE * fp, t_s8 *str, t_s32 size, int *lineno) +{ + char *start, *end; + int out, next_line; + + if (!fp || !str) + return NULL; + + do { +read_line: + if (!fgets(str, size, fp)) + break; + start = str; + start[size - 1] = '\0'; + end = start + strlen(str); + (*lineno)++; + + out = 1; + while (out && (start < end)) { + next_line = 0; + /* Remove empty lines and lines starting with # */ + switch (start[0]) { + case ' ': /* White space */ + case '\t': /* Tab */ + start++; + break; + case '#': + case '\n': + case '\0': + next_line = 1; + break; + case '\r': + if (start[1] == '\n') + next_line = 1; + else + start++; + break; + default: + out = 0; + break; + } + if (next_line) + goto read_line; + } + + /* Remove # comments unless they are within a double quoted + * string. Remove trailing white space. */ + if ((end = strstr(start, "\""))) { + if (!(end = strstr(end + 1, "\""))) + end = start; + } else + end = start; + + if ((end = strstr(end + 1, "#"))) + *end-- = '\0'; + else + end = start + strlen(start) - 1; + + out = 1; + while (out && (start < end)) { + switch (*end) { + case ' ': /* White space */ + case '\t': /* Tab */ + case '\n': + case '\r': + *end = '\0'; + end--; + break; + default: + out = 0; + break; + } + } + + if (start == '\0') + continue; + + return start; + } while (1); + + return NULL; +} + +/** + * @brief parse hex data + * @param fp File handler + * @param dst A pointer to receive hex data + * @return length of hex data + */ +int +fparse_for_hex(FILE * fp, t_u8 *dst) +{ + t_s8 *ptr; + t_u8 *dptr; + t_s8 buf[256]; + + dptr = dst; + while (fgets(buf, sizeof(buf), fp)) { + ptr = buf; + + while (*ptr) { + /* skip leading spaces */ + while (*ptr && (isspace(*ptr) || *ptr == '\t')) + ptr++; + + /* skip blank lines and lines beginning with '#' */ + if (*ptr == '\0' || *ptr == '#') + break; + + if (isxdigit(*ptr)) { + ptr = convert2hex(ptr, dptr++); + } else { + /* Invalid character on data line */ + ptr++; + } + } + } + + return (dptr - dst); +} + +#define STACK_NBYTES 100 /**< Number of bytes in stack */ +#define MAX_BYTESEQ 6 /**< Maximum byte sequence */ +#define TYPE_DNUM 1 /**< decimal number */ +#define TYPE_BYTESEQ 2 /**< byte sequence */ +#define MAX_OPERAND 0x40 /**< Maximum operands */ +#define TYPE_EQ (MAX_OPERAND+1) /**< byte comparison: == operator */ +#define TYPE_EQ_DNUM (MAX_OPERAND+2) /**< decimal comparison: =d operator */ +#define TYPE_EQ_BIT (MAX_OPERAND+3) /**< bit comparison: =b operator */ +#define TYPE_AND (MAX_OPERAND+4) /**< && operator */ +#define TYPE_OR (MAX_OPERAND+5) /**< || operator */ + +typedef struct { + t_u16 sp; /**< Stack pointer */ + t_u8 byte[STACK_NBYTES]; /**< Stack */ +} mstack_t; + +typedef struct { + t_u8 type; /**< Type */ + t_u8 reserve[3]; /**< so 4-byte align val array */ + /* byte sequence is the largest among all the operands and operators. */ + /* byte sequence format: 1 byte of num of bytes, then variable num bytes */ + t_u8 val[MAX_BYTESEQ + 1];/**< Value */ +} op_t; + +/** + * @brief push data to stack + * + * @param s a pointer to mstack_t structure + * + * @param nbytes number of byte to push to stack + * + * @param val a pointer to data buffer + * + * @return TRUE-- sucess , FALSE -- fail + * + */ +static int +push_n(mstack_t * s, t_u8 nbytes, t_u8 *val) +{ + if ((s->sp + nbytes) < STACK_NBYTES) { + memcpy((void *)(s->byte + s->sp), (const void *)val, + (size_t) nbytes); + s->sp += nbytes; + /* printf("push: n %d sp %d\n", nbytes, s->sp); */ + return TRUE; + } else /* stack full */ + return FALSE; +} + +/** + * @brief push data to stack + * + * @param s a pointer to mstack_t structure + * + * @param op a pointer to op_t structure + * + * @return TRUE-- sucess , FALSE -- fail + * + */ +static int +push(mstack_t * s, op_t * op) +{ + t_u8 nbytes; + switch (op->type) { + case TYPE_DNUM: + if (push_n(s, 4, op->val)) + return (push_n(s, 1, &op->type)); + return FALSE; + case TYPE_BYTESEQ: + nbytes = op->val[0]; + if (push_n(s, nbytes, op->val + 1) && + push_n(s, 1, op->val) && push_n(s, 1, &op->type)) + return TRUE; + return FALSE; + default: + return (push_n(s, 1, &op->type)); + } +} + +/** + * @brief parse RPN string + * + * @param s a pointer to Null-terminated string to scan. + * + * @param first_time a pointer to return first_time + * + * @return A pointer to the last token found in string. + * NULL is returned when there are no more tokens to be found. + * + */ +static char * +getop(char *s, int *first_time) +{ + const char delim[] = " \t\n"; + char *p; + if (*first_time) { + p = strtok(s, delim); + *first_time = FALSE; + } else { + p = strtok(NULL, delim); + } + return (p); +} + +/** + * @brief Verify hex digit. + * + * @param c input ascii char + * @param h a pointer to return integer value of the digit char. + * @return TURE -- c is hex digit, FALSE -- c is not hex digit. + */ +static int +ishexdigit(char c, t_u8 *h) +{ + if (c >= '0' && c <= '9') { + *h = c - '0'; + return (TRUE); + } else if (c >= 'a' && c <= 'f') { + *h = c - 'a' + 10; + return (TRUE); + } else if (c >= 'A' && c <= 'F') { + *h = c - 'A' + 10; + return (TRUE); + } + return (FALSE); +} + +/** + * @brief convert hex string to integer. + * + * @param s A pointer to hex string, string length up to 2 digits. + * @return integer value. + */ +static t_u8 +hex_atoi(char *s) +{ + int i; + t_u8 digit; /* digital value */ + t_u8 t = 0; /* total value */ + + for (i = 0, t = 0; ishexdigit(s[i], &digit) && i < 2; i++) + t = 16 * t + digit; + return (t); +} + +/** + * @brief Parse byte sequence in hex format string to a byte sequence. + * + * @param opstr A pointer to byte sequence in hex format string, with ':' as delimiter between two byte. + * @param val A pointer to return byte sequence string + * @return NA + */ +static void +parse_hex(char *opstr, t_u8 *val) +{ + char delim = ':'; + char *p; + char *q; + t_u8 i; + + /* +1 is for skipping over the preceding h character. */ + p = opstr + 1; + + /* First byte */ + val[1] = hex_atoi(p++); + + /* Parse subsequent bytes. */ + /* Each byte is preceded by the : character. */ + for (i = 1; *p; i++) { + q = strchr(p, delim); + if (!q) + break; + p = q + 1; + val[i + 1] = hex_atoi(p); + } + /* Set num of bytes */ + val[0] = i; +} + +/** + * @brief str2bin, convert RPN string to binary format + * + * @param str A pointer to rpn string + * @param stack A pointer to mstack_t structure + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +str2bin(char *str, mstack_t * stack) +{ + int first_time; + char *opstr; + op_t op; /* operator/operand */ + int dnum; + int ret = MLAN_STATUS_SUCCESS; + + memset(stack, 0, sizeof(mstack_t)); + first_time = TRUE; + while ((opstr = getop(str, &first_time)) != NULL) { + if (isdigit(*opstr)) { + op.type = TYPE_DNUM; + dnum = cpu_to_le32(atoi(opstr)); + memcpy((t_u8 *)op.val, &dnum, sizeof(dnum)); + if (!push(stack, &op)) { + printf("push decimal number failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } else if (*opstr == 'h') { + op.type = TYPE_BYTESEQ; + parse_hex(opstr, op.val); + if (!push(stack, &op)) { + printf("push byte sequence failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } else if (!strcmp(opstr, "==")) { + op.type = TYPE_EQ; + if (!push(stack, &op)) { + printf("push byte cmp operator failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } else if (!strcmp(opstr, "=d")) { + op.type = TYPE_EQ_DNUM; + if (!push(stack, &op)) { + printf("push decimal cmp operator failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } else if (!strcmp(opstr, "=b")) { + op.type = TYPE_EQ_BIT; + if (!push(stack, &op)) { + printf("push bit cmp operator failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } else if (!strcmp(opstr, "&&")) { + op.type = TYPE_AND; + if (!push(stack, &op)) { + printf("push AND operator failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } else if (!strcmp(opstr, "||")) { + op.type = TYPE_OR; + if (!push(stack, &op)) { + printf("push OR operator failed\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } else { + printf("Unknown operand\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + } + return ret; +} + +/** + * @brief Converts colon separated MAC address to hex value + * + * @param mac A pointer to the colon separated MAC string + * @param raw A pointer to the hex data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * MAC_BROADCAST - if breadcast mac + * MAC_MULTICAST - if multicast mac + */ +static int +mac2raw(char *mac, t_u8 *raw) +{ + unsigned int temp_raw[ETH_ALEN]; + int num_tokens = 0; + int i; + + if (strlen(mac) != ((2 * ETH_ALEN) + (ETH_ALEN - 1))) { + return MLAN_STATUS_FAILURE; + } + num_tokens = sscanf(mac, "%2x:%2x:%2x:%2x:%2x:%2x", + temp_raw + 0, temp_raw + 1, temp_raw + 2, + temp_raw + 3, temp_raw + 4, temp_raw + 5); + if (num_tokens != ETH_ALEN) { + return MLAN_STATUS_FAILURE; + } + for (i = 0; i < num_tokens; i++) + raw[i] = (t_u8)temp_raw[i]; + + if (memcmp(raw, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) { + return MAC_BROADCAST; + } else if (raw[0] & 0x01) { + return MAC_MULTICAST; + } + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Parse function for a configuration line + * + * @param s Storage buffer for data + * @param size Maximum size of data + * @param stream File stream pointer + * @param line Pointer to current line within the file + * @param _pos Output string or NULL + * @return String or NULL + */ +static char * +config_get_line(char *s, int size, FILE * stream, int *line, char **_pos) +{ + *_pos = mlan_config_get_line(stream, s, size, line); + return *_pos; +} + +/** + * @brief Parses a command line + * + * @param line The line to parse + * @param args Pointer to the argument buffer to be filled in + * @return Number of arguments in the line or EOF + */ +static int +parse_line(char *line, char *args[]) +{ + int arg_num = 0; + int is_start = 0; + int is_quote = 0; + int length = 0; + int i = 0; + + arg_num = 0; + length = strlen(line); + /* Process line */ + + /* Find number of arguments */ + is_start = 0; + is_quote = 0; + for (i = 0; i < length; i++) { + /* Ignore leading spaces */ + if (is_start == 0) { + if (line[i] == ' ') { + continue; + } else if (line[i] == '\t') { + continue; + } else if (line[i] == '\n') { + break; + } else { + is_start = 1; + args[arg_num] = &line[i]; + arg_num++; + } + } + if (is_start == 1) { + /* Ignore comments */ + if (line[i] == '#') { + if (is_quote == 0) { + line[i] = '\0'; + arg_num--; + } + break; + } + /* Separate by '=' */ + if (line[i] == '=') { + line[i] = '\0'; + is_start = 0; + continue; + } + /* Separate by ',' */ + if (line[i] == ',') { + line[i] = '\0'; + is_start = 0; + continue; + } + /* Change ',' to ' ', but not inside quotes */ + if ((line[i] == ',') && (is_quote == 0)) { + line[i] = ' '; + continue; + } + } + /* Remove newlines */ + if (line[i] == '\n') { + line[i] = '\0'; + } + /* Check for quotes */ + if (line[i] == '"') { + is_quote = (is_quote == 1) ? 0 : 1; + continue; + } + if (((line[i] == ' ') || (line[i] == '\t')) && (is_quote == 0)) { + line[i] = '\0'; + is_start = 0; + continue; + } + } + return arg_num; +} + +#define FILTER_BYTESEQ TYPE_EQ /**< byte sequence */ +#define FILTER_DNUM TYPE_EQ_DNUM /**< decimal number */ +#define FILTER_BITSEQ TYPE_EQ_BIT /**< bit sequence */ +#define FILTER_TEST FILTER_BITSEQ+1 /**< test */ + +#define NAME_TYPE 1 /**< Field name 'type' */ +#define NAME_PATTERN 2 /**< Field name 'pattern' */ +#define NAME_OFFSET 3 /**< Field name 'offset' */ +#define NAME_NUMBYTE 4 /**< Field name 'numbyte' */ +#define NAME_REPEAT 5 /**< Field name 'repeat' */ +#define NAME_BYTE 6 /**< Field name 'byte' */ +#define NAME_MASK 7 /**< Field name 'mask' */ +#define NAME_DEST 8 /**< Field name 'dest' */ + +static struct mef_fields { + t_s8 *name; + /**< Name */ + t_s8 nameid; + /**< Name Id. */ +} mef_fields[] = { + { + "type", NAME_TYPE}, { + "pattern", NAME_PATTERN}, { + "offset", NAME_OFFSET}, { + "numbyte", NAME_NUMBYTE}, { + "repeat", NAME_REPEAT}, { + "byte", NAME_BYTE}, { + "mask", NAME_MASK}, { + "dest", NAME_DEST} +}; + +/** + * @brief get filter data + * + * @param fp A pointer to file stream + * @param ln A pointer to line number + * @param buf A pointer to hostcmd data + * @param size A pointer to the return size of hostcmd buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +mlan_get_filter_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size) +{ + t_s32 errors = 0, i; + t_s8 line[256], *pos, *pos1; + t_u16 type = 0; + t_u32 pattern = 0; + t_u16 repeat = 0; + t_u16 offset = 0; + t_s8 byte_seq[50]; + t_s8 mask_seq[50]; + t_u16 numbyte = 0; + t_s8 type_find = 0; + t_s8 pattern_find = 0; + t_s8 offset_find = 0; + t_s8 numbyte_find = 0; + t_s8 repeat_find = 0; + t_s8 byte_find = 0; + t_s8 mask_find = 0; + t_s8 dest_find = 0; + t_s8 dest_seq[50]; + + *size = 0; + while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) { + if (strcmp(pos, "}") == 0) { + break; + } + pos1 = strchr(pos, '='); + if (pos1 == NULL) { + printf("Line %d: Invalid mef_filter line '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos1++ = '\0'; + for (i = 0; (t_u32)i < NELEMENTS(mef_fields); i++) { + if (strncmp + (pos, mef_fields[i].name, + strlen(mef_fields[i].name)) == 0) { + switch (mef_fields[i].nameid) { + case NAME_TYPE: + type = a2hex_or_atoi(pos1); + if ((type != FILTER_DNUM) && + (type != FILTER_BYTESEQ) + && (type != FILTER_BITSEQ) && + (type != FILTER_TEST)) { + printf("Invalid filter type:%d\n", type); + return MLAN_STATUS_FAILURE; + } + type_find = 1; + break; + case NAME_PATTERN: + pattern = a2hex_or_atoi(pos1); + pattern_find = 1; + break; + case NAME_OFFSET: + offset = a2hex_or_atoi(pos1); + offset_find = 1; + break; + case NAME_NUMBYTE: + numbyte = a2hex_or_atoi(pos1); + numbyte_find = 1; + break; + case NAME_REPEAT: + repeat = a2hex_or_atoi(pos1); + repeat_find = 1; + break; + case NAME_BYTE: + memset(byte_seq, 0, sizeof(byte_seq)); + strncpy(byte_seq, pos1, + (sizeof(byte_seq) - 1)); + byte_find = 1; + break; + case NAME_MASK: + memset(mask_seq, 0, sizeof(mask_seq)); + strncpy(mask_seq, pos1, + (sizeof(mask_seq) - 1)); + mask_find = 1; + break; + case NAME_DEST: + memset(dest_seq, 0, sizeof(dest_seq)); + strncpy(dest_seq, pos1, + (sizeof(dest_seq) - 1)); + dest_find = 1; + break; + } + break; + } + } + if (i == NELEMENTS(mef_fields)) { + printf("Line %d: unknown mef field '%s'.\n", + *line, pos); + errors++; + } + } + if (type_find == 0) { + printf("Can not find filter type\n"); + return MLAN_STATUS_FAILURE; + } + switch (type) { + case FILTER_DNUM: + if (!pattern_find || !offset_find || !numbyte_find) { + printf("Missing field for FILTER_DNUM: pattern=%d,offset=%d,numbyte=%d\n", pattern_find, offset_find, numbyte_find); + return MLAN_STATUS_FAILURE; + } + memset(line, 0, sizeof(line)); + snprintf(line, sizeof(line), "%d %d %d =d ", pattern, offset, + numbyte); + break; + case FILTER_BYTESEQ: + if (!byte_find || !offset_find || !repeat_find) { + printf("Missing field for FILTER_BYTESEQ: byte=%d,offset=%d,repeat=%d\n", byte_find, offset_find, repeat_find); + return MLAN_STATUS_FAILURE; + } + memset(line, 0, sizeof(line)); + snprintf(line, sizeof(line), "%d h%s %d == ", repeat, byte_seq, + offset); + break; + case FILTER_BITSEQ: + if (!byte_find || !offset_find || !mask_find) { + printf("Missing field for FILTER_BITSEQ: byte=%d,offset=%d,mask_find=%d\n", byte_find, offset_find, mask_find); + return MLAN_STATUS_FAILURE; + } + if (strlen(byte_seq) != strlen(mask_seq)) { + printf("byte string's length is different with mask's length!\n"); + return MLAN_STATUS_FAILURE; + } + memset(line, 0, sizeof(line)); + snprintf(line, sizeof(line), "h%s %d h%s =b ", byte_seq, offset, + mask_seq); + break; + case FILTER_TEST: + if (!byte_find || !offset_find || !repeat_find || !dest_find) { + printf("Missing field for FILTER_TEST: byte=%d,offset=%d,repeat=%d,dest=%d\n", byte_find, offset_find, repeat_find, dest_find); + return MLAN_STATUS_FAILURE; + } + memset(line, 0, sizeof(line)); + snprintf(line, sizeof(line), "h%s %d h%s %d ", dest_seq, repeat, + byte_seq, offset); + break; + } + memcpy(buf, line, strlen(line)); + *size = strlen(line); + return MLAN_STATUS_SUCCESS; +} + +#define NAME_MODE 1 /**< Field name 'mode' */ +#define NAME_ACTION 2 /**< Field name 'action' */ +#define NAME_FILTER_NUM 3 /**< Field name 'filter_num' */ +#define NAME_RPN 4 /**< Field name 'RPN' */ +static struct mef_entry_fields { + t_s8 *name; + /**< Name */ + t_s8 nameid; + /**< Name id */ +} mef_entry_fields[] = { + { + "mode", NAME_MODE}, { + "action", NAME_ACTION}, { + "filter_num", NAME_FILTER_NUM}, { +"RPN", NAME_RPN},}; + +typedef struct _MEF_ENTRY { + /** Mode */ + t_u8 Mode; + /** Size */ + t_u8 Action; + /** Size of expression */ + t_u16 ExprSize; +} MEF_ENTRY; + +/** + * @brief get mef_entry data + * + * @param fp A pointer to file stream + * @param ln A pointer to line number + * @param buf A pointer to hostcmd data + * @param size A pointer to the return size of hostcmd buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +mlan_get_mef_entry_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size) +{ + t_s8 line[256], *pos, *pos1; + t_u8 mode, action, filter_num = 0; + t_s8 rpn[256]; + t_s8 mode_find = 0; + t_s8 action_find = 0; + t_s8 filter_num_find = 0; + t_s8 rpn_find = 0; + t_s8 rpn_str[256]; + int rpn_len = 0; + t_s8 filter_name[50]; + t_s8 name_found = 0; + t_u16 len = 0; + int i; + int first_time = TRUE; + char *opstr; + t_s8 filter_action[10]; + t_s32 errors = 0; + MEF_ENTRY *pMefEntry = (MEF_ENTRY *) buf; + mstack_t stack; + while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) { + if (strcmp(pos, "}") == 0) { + break; + } + pos1 = strchr(pos, '='); + if (pos1 == NULL) { + printf("Line %d: Invalid mef_entry line '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos1++ = '\0'; + if (!mode_find || !action_find || !filter_num_find || !rpn_find) { + for (i = 0; + (unsigned int)i < NELEMENTS(mef_entry_fields); + i++) { + if (strncmp + (pos, mef_entry_fields[i].name, + strlen(mef_entry_fields[i].name)) == 0) { + switch (mef_entry_fields[i].nameid) { + case NAME_MODE: + mode = a2hex_or_atoi(pos1); + if (mode & ~0x7) { + printf("invalid mode=%d\n", mode); + return MLAN_STATUS_FAILURE; + } + pMefEntry->Mode = mode; + mode_find = 1; + break; + case NAME_ACTION: + action = a2hex_or_atoi(pos1); + if (action & ~0xff) { + printf("invalid action=%d\n", action); + return MLAN_STATUS_FAILURE; + } + pMefEntry->Action = action; + action_find = 1; + break; + case NAME_FILTER_NUM: + filter_num = + a2hex_or_atoi(pos1); + filter_num_find = 1; + break; + case NAME_RPN: + memset(rpn, 0, sizeof(rpn)); + strncpy(rpn, pos1, + (sizeof(rpn) - 1)); + rpn_find = 1; + break; + } + break; + } + } + if (i == NELEMENTS(mef_fields)) { + printf("Line %d: unknown mef_entry field '%s'.\n", *line, pos); + return MLAN_STATUS_FAILURE; + } + } + if (mode_find && action_find && filter_num_find && rpn_find) { + for (i = 0; i < filter_num; i++) { + opstr = getop(rpn, &first_time); + if (opstr == NULL) + break; + snprintf(filter_name, sizeof(filter_name), + "%s={", opstr); + name_found = 0; + while ((pos = + mlan_config_get_line(fp, line, + sizeof(line), + ln))) { + if (strncmp + (pos, filter_name, + strlen(filter_name)) == 0) { + name_found = 1; + break; + } + } + if (!name_found) { + fprintf(stderr, + "mlanconfig: %s not found in file\n", + filter_name); + return MLAN_STATUS_FAILURE; + } + if (MLAN_STATUS_FAILURE == + mlan_get_filter_data(fp, ln, + (t_u8 *)(rpn_str + + rpn_len), + &len)) + break; + rpn_len += len; + if (i > 0) { + memcpy(rpn_str + rpn_len, filter_action, + strlen(filter_action)); + rpn_len += strlen(filter_action); + } + opstr = getop(rpn, &first_time); + if (opstr == NULL) + break; + memset(filter_action, 0, sizeof(filter_action)); + snprintf(filter_action, sizeof(filter_action), + "%s ", opstr); + } + /* Remove the last space */ + if (rpn_len > 0) { + rpn_len--; + rpn_str[rpn_len] = 0; + } + if (MLAN_STATUS_FAILURE == str2bin(rpn_str, &stack)) { + printf("Fail on str2bin!\n"); + return MLAN_STATUS_FAILURE; + } + *size = sizeof(MEF_ENTRY); + pMefEntry->ExprSize = cpu_to_le16(stack.sp); + memmove(buf + sizeof(MEF_ENTRY), stack.byte, stack.sp); + *size += stack.sp; + break; + } else if (mode_find && action_find && filter_num_find && + (filter_num == 0)) { + pMefEntry->ExprSize = 0; + *size = sizeof(MEF_ENTRY); + break; + } + } + return MLAN_STATUS_SUCCESS; +} + +#define MEFCFG_CMDCODE 0x009a +/** + * @brief Process mef cfg + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_mef_cfg(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + t_s8 line[256], cmdname[256], *pos; + int cmdname_found = 0, name_found = 0; + int ln = 0; + int ret = MLAN_STATUS_SUCCESS; + int i; + t_u8 *buf; + t_u16 buf_len = 0; + t_u16 len; + struct iwreq iwr; + HostCmd_DS_MEF_CFG *mefcmd; + HostCmd_DS_GEN *hostcmd; + FILE *fp = NULL; + + if (argc < 4) { + printf("Error: invalid no of arguments\n"); + printf("Syntax: ./mlanconfig eth1 mefcfg <mef.conf>\n"); + exit(1); + } + if (get_priv_ioctl("hostcmd", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + snprintf(cmdname, sizeof(cmdname), "%s={", argv[2]); + cmdname_found = 0; + if ((fp = fopen(argv[3], "r")) == NULL) { + fprintf(stderr, "Cannot open file %s\n", argv[4]); + exit(1); + } + + buf = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (buf == NULL) { + fclose(fp); + fprintf(stderr, "Cannot alloc memory\n"); + exit(1); + } + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + + hostcmd = (HostCmd_DS_GEN *)(buf); + hostcmd->command = cpu_to_le16(MEFCFG_CMDCODE); + mefcmd = (HostCmd_DS_MEF_CFG *)(buf + S_DS_GEN); + buf_len = sizeof(HostCmd_DS_MEF_CFG) + S_DS_GEN; + + while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) { + if (strcmp(pos, cmdname) == 0) { + cmdname_found = 1; + snprintf(cmdname, sizeof(cmdname), "Criteria="); + name_found = 0; + while ((pos = + mlan_config_get_line(fp, line, sizeof(line), + &ln))) { + if (strncmp(pos, cmdname, strlen(cmdname)) == 0) { + name_found = 1; + mefcmd->Criteria = + a2hex_or_atoi(pos + + strlen(cmdname)); + break; + } + } + if (!name_found) { + fprintf(stderr, + "mlanconfig: criteria not found in file '%s'\n", + argv[3]); + break; + } + snprintf(cmdname, sizeof(cmdname), "NumEntries="); + name_found = 0; + while ((pos = + mlan_config_get_line(fp, line, sizeof(line), + &ln))) { + if (strncmp(pos, cmdname, strlen(cmdname)) == 0) { + name_found = 1; + mefcmd->NumEntries = + a2hex_or_atoi(pos + + strlen(cmdname)); + break; + } + } + if (!name_found) { + fprintf(stderr, + "mlanconfig: NumEntries not found in file '%s'\n", + argv[3]); + break; + } + for (i = 0; i < mefcmd->NumEntries; i++) { + snprintf(cmdname, sizeof(cmdname), + "mef_entry_%d={", i); + name_found = 0; + while ((pos = + mlan_config_get_line(fp, line, + sizeof(line), + &ln))) { + if (strncmp + (pos, cmdname, + strlen(cmdname)) == 0) { + name_found = 1; + break; + } + } + if (!name_found) { + fprintf(stderr, + "mlanconfig: %s not found in file '%s'\n", + cmdname, argv[3]); + break; + } + if (MLAN_STATUS_FAILURE == + mlan_get_mef_entry_data(fp, &ln, + buf + buf_len, + &len)) { + ret = MLAN_STATUS_FAILURE; + break; + } + buf_len += len; + } + break; + } + } + fclose(fp); + /* hexdump("mef_cfg",buf,buf_len, ' '); */ + if (!cmdname_found) + fprintf(stderr, + "mlanconfig: cmdname '%s' not found in file '%s'\n", + argv[4], argv[3]); + + if (!cmdname_found || !name_found) { + ret = MLAN_STATUS_FAILURE; + goto mef_exit; + } + hostcmd->size = cpu_to_le16(buf_len); + mefcmd->Criteria = cpu_to_le32(mefcmd->Criteria); + mefcmd->NumEntries = cpu_to_le16(mefcmd->NumEntries); + + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = buf; + iwr.u.data.length = buf_len; + iwr.u.data.flags = 0; + if (ioctl(sockfd, ioctl_val, &iwr)) { + fprintf(stderr, "mlanconfig: MEFCFG is not supported by %s\n", + dev_name); + ret = MLAN_STATUS_FAILURE; + goto mef_exit; + } + ret = process_host_cmd_resp(buf); + +mef_exit: + if (buf) + free(buf); + return ret; + +} + +/** + * @brief Process transmission of mgmt frames + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_mgmt_frame_tx(int argc, char *argv[]) +{ + struct ifreq ifr; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, arg_num = 0, ret = 0, i = 0; + char *args[100], *pos = NULL, mac_addr[20]; + t_u8 peer_mac[ETH_ALEN]; + t_u16 data_len = 0, subtype = 0; + wlan_mgmt_frame_tx *pmgmt_frame; + t_u8 *buffer = NULL; + pkt_header *hdr = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX mgmtframetx <config/pkt.conf>\n"); + exit(1); + } + + data_len = sizeof(wlan_mgmt_frame_tx); + + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + hdr = (pkt_header *)buffer; + pmgmt_frame = (wlan_mgmt_frame_tx *)(buffer + sizeof(pkt_header)); + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + goto done; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + arg_num = parse_line(line, args); + if (strcmp(args[0], "PktSubType") == 0) { + subtype = (t_u16)A2HEXDECIMAL(args[1]); + pmgmt_frame->frm_ctl |= subtype << 4; + } else if (strncmp(args[0], "Addr", 4) == 0) { + strncpy(mac_addr, args[1], 20); + if ((ret = + mac2raw(mac_addr, + peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret + == + MAC_BROADCAST ? "Broadcast" : + "Multicast"); + goto done; + } + i = atoi(args[0] + 4); + switch (i) { + case 1: + memcpy(pmgmt_frame->addr1, peer_mac, ETH_ALEN); + break; + case 2: + memcpy(pmgmt_frame->addr2, peer_mac, ETH_ALEN); + break; + case 3: + memcpy(pmgmt_frame->addr3, peer_mac, ETH_ALEN); + break; + case 4: + memcpy(pmgmt_frame->addr4, peer_mac, ETH_ALEN); + break; + } + } else if (strcmp(args[0], "Data") == 0) { + for (i = 0; i < arg_num - 1; i++) + pmgmt_frame->payload[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + data_len += arg_num - 1; + } + } + pmgmt_frame->frm_len = data_len - sizeof(pmgmt_frame->frm_len); +#define MRVL_PKT_TYPE_MGMT_FRAME 0xE5 + hdr->pkt_len = data_len; + hdr->TxPktType = MRVL_PKT_TYPE_MGMT_FRAME; + hdr->TxControl = 0; + hexdump("Frame Tx", buffer, data_len + sizeof(pkt_header), ' '); + /* Send collective command */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)buffer; + + /* Perform ioctl */ + if (ioctl(sockfd, FRAME_TX_IOCTL, &ifr)) { + perror(""); + printf("ERR:Could not send management frame.\n"); + } else { + printf("Mgmt Frame sucessfully sent.\n"); + } + +done: + if (config_file) + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Performs the ioctl operation to send the command to + * the driver. + * + * @param cmd_buf Pointer to the command buffer + * @param buf_size Size of the allocated command buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int +tdls_ioctl(t_u8 *cmd_buf, t_u16 buf_size) +{ + struct ifreq ifr; + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd_buf; + + /* Perform ioctl */ + if (ioctl(sockfd, TDLS_IOCTL, &ifr)) { + perror(""); + return MLAN_STATUS_FAILURE; + } + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief enable/disable tdls config + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_config(int argc, char *argv[]) +{ + + tdls_config *param_buf = NULL; + int ret = 0; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_config <0/1>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_config); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_config *) buffer; + param_buf->action = ACTION_TDLS_CONFIG; + + param_buf->data = (t_u16)A2HEXDECIMAL(argv[3]); + if ((param_buf->data != 0) && (param_buf->data != 1)) { + printf("ERR:Incorrect arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_config <0/1>\n"); + goto done; + } + hexdump("tdls_config ", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS %s successful.\n", + (param_buf->data) ? "enable" : "disable"); + } else { + printf("ERR:TDLS %s failed.\n", + (param_buf->data) ? "enable" : "disable"); + } + +done: + if (buffer) + free(buffer); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_setinfo + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_setinfo(int argc, char *argv[]) +{ + tdls_setinfo *param_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, arg_num = 0, ret = 0, i = 0, cmd_found = 0, pairwise_index = + 0, akm_index = 0, pmkid_index = 0; + char *args[30], *pos = NULL; + t_u16 cmd_len = 0, tlv_len = 0, tlv_len_rsn = 0, tlv_len_supp_chan = + 0, tlv_len_domain = 0; + t_u16 no_of_sub_band = 0, no_of_supp_chan_sub_band = + 0, pairwise_offset = 0, akm_offset = + 0, num_of_regulatory_class = 0, tlv_len_reg_class; + t_u16 akm_count = 0, pmk_count = 0, rsn_cap = 0; + t_u8 *buffer = NULL; + char country[COUNTRY_CODE_LEN]; + tlvbuf_DomainParamSet_t *domain = NULL; + tlvbuf_SupportedChannels_t *supp_chan = NULL; + tlvbuf_RegulatoryClass_t *reg_class = NULL; + tlvbuf_HTCap_t *tlv_ht_cap = NULL; + tlvbuf_RsnParamSet_t *rsn_ie = NULL; + tlvbuf_HTInfo_t *tlv_ht_info = NULL; + t_u8 pairwise_cipher_suite[PAIRWISE_CIPHER_SUITE_LEN]; + t_u8 akm_suite[AKM_SUITE_LEN]; + t_u8 pmkid[PMKID_LEN]; + tlvbuf_VHTCap_t *tlv_vht_cap = NULL; + tlvbuf_VHTOpra_t *tlv_vht_oper = NULL; + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_setinfo <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_setinfo); + + buffer = (t_u8 *)malloc(MRVDRV_SIZE_OF_CMD_BUFFER); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + param_buf = (tdls_setinfo *)buffer; + param_buf->action = ACTION_TDLS_SETINFO; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + goto done; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + arg_num = parse_line(line, args); + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + if (strcmp(args[0], "CapInfo") == 0) { + param_buf->cap_info = (t_u16)A2HEXDECIMAL(args[1]); + param_buf->cap_info = cpu_to_le16(param_buf->cap_info); + } else if (strcmp(args[0], "Rate") == 0) { + tlvbuf_RatesParamSet_t *tlv = NULL; + /* Append a new TLV */ + tlv_len = sizeof(tlvbuf_RatesParamSet_t) + arg_num - 1; + tlv = (tlvbuf_RatesParamSet_t *)(buffer + cmd_len); + cmd_len += tlv_len; + /* Set TLV fields */ + tlv->tag = TLV_TYPE_RATES; + tlv->length = arg_num - 1; + for (i = 0; i < tlv->length; i++) { + tlv->rates[i] = (t_u8)A2HEXDECIMAL(args[i + 1]); + } + endian_convert_tlv_header_out(tlv); + } else if (strcmp(args[0], "QosInfo") == 0) { + tlvbuf_QosInfo_t *tlv = NULL; + /* Append a new TLV */ + tlv_len = sizeof(tlvbuf_QosInfo_t); + tlv = (tlvbuf_QosInfo_t *)(buffer + cmd_len); + cmd_len += tlv_len; + /* Set TLV fields */ + tlv->tag = TLV_TYPE_QOSINFO; + tlv->length = sizeof(tlvbuf_QosInfo_t) - TLVHEADER_LEN; + tlv->u.qos_info_byte = (t_u8)A2HEXDECIMAL(args[1]); + if ((tlv->u.qos_info_byte != 0) && + (tlv->u.qos_info_byte != 0x0F)) { + printf("Invalid QosInfo. Should be 0x00 or 0x0F.\n"); + goto done; + } + endian_convert_tlv_header_out(tlv); + } else if (strcmp(args[0], "ExtendCapabilities") == 0) { + tlvbuf_ExtCap_t *tlv = NULL; + /* Append a new TLV */ + tlv_len = sizeof(tlvbuf_ExtCap_t) + arg_num - 1; + tlv = (tlvbuf_ExtCap_t *)(buffer + cmd_len); + cmd_len += tlv_len; + /* Set TLV fields */ + tlv->tag = TLV_TYPE_EXTCAP; + tlv->length = arg_num - 1; + for (i = 0; i < tlv->length; i++) { + tlv->ext_cap[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + } + endian_convert_tlv_header_out(tlv); + } else if (strcmp(args[0], "HTCapability") == 0) { + /* Append a new TLV */ + tlv_ht_cap = (tlvbuf_HTCap_t *)(buffer + cmd_len); + tlv_len = sizeof(tlvbuf_HTCap_t); + tlv_ht_cap->tag = TLV_TYPE_HT_CAP; + tlv_ht_cap->length = + sizeof(tlvbuf_HTCap_t) - TLVHEADER_LEN; + cmd_len += tlv_len; + endian_convert_tlv_header_out(tlv_ht_cap); + } else if (strcmp(args[0], "HTCapabilityInfo") == 0) { + tlv_ht_cap->ht_cap.ht_cap_info = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_ht_cap->ht_cap.ht_cap_info = + cpu_to_le16(tlv_ht_cap->ht_cap.ht_cap_info); + } else if (strcmp(args[0], "AMPDUParam") == 0) { + tlv_ht_cap->ht_cap.ampdu_param = + (t_u8)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "SupportedMCSSet") == 0) { + for (i = 0; i < MCS_SET_LEN; i++) + tlv_ht_cap->ht_cap.supported_mcs_set[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + } else if (strcmp(args[0], "HTExtCapability") == 0) { + tlv_ht_cap->ht_cap.ht_ext_cap = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_ht_cap->ht_cap.ht_ext_cap = + cpu_to_le16(tlv_ht_cap->ht_cap.ht_ext_cap); + } else if (strcmp(args[0], "TxBfCapability") == 0) { + tlv_ht_cap->ht_cap.tx_bf_cap = + (t_u32)A2HEXDECIMAL(args[1]); + tlv_ht_cap->ht_cap.tx_bf_cap = + cpu_to_le32(tlv_ht_cap->ht_cap.tx_bf_cap); + } else if (strcmp(args[0], "AntennaSel") == 0) { + tlv_ht_cap->ht_cap.asel = (t_u8)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "HTInformation") == 0) { + /* Append a new TLV */ + tlv_ht_info = (tlvbuf_HTInfo_t *)(buffer + cmd_len); + tlv_len = sizeof(tlvbuf_HTInfo_t); + tlv_ht_info->tag = TLV_TYPE_HT_INFO; + tlv_ht_info->length = + sizeof(tlvbuf_HTInfo_t) - TLVHEADER_LEN; + cmd_len += tlv_len; + endian_convert_tlv_header_out(tlv_ht_info); + } else if (strcmp(args[0], "PrimaryChannel") == 0) { + tlv_ht_info->ht_info.pri_chan = A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "Field2") == 0) { + tlv_ht_info->ht_info.field2 = A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "Field3") == 0) { + tlv_ht_info->ht_info.field3 = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_ht_info->ht_info.field3 = + cpu_to_le16(tlv_ht_info->ht_info.field3); + } else if (strcmp(args[0], "Field4") == 0) { + tlv_ht_info->ht_info.field4 = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_ht_info->ht_info.field4 = + cpu_to_le16(tlv_ht_info->ht_info.field4); + } else if (strcmp(args[0], "BasicMCSSet") == 0) { + if ((arg_num - 1) != MCS_SET_LEN) { + printf("Incorrect number of arguments for BasicMCSSet.\n"); + goto done; + } + for (i = 0; i < MCS_SET_LEN; i++) + tlv_ht_info->ht_info.basic_mcs_set[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + } else if (strcmp(args[0], "2040BSSCoex") == 0) { + tlvbuf_2040BSSCo_t *tlv = NULL; + /* Append a new TLV */ + tlv_len = sizeof(tlvbuf_2040BSSCo_t); + tlv = (tlvbuf_2040BSSCo_t *)(buffer + cmd_len); + cmd_len += tlv_len; + /* Set TLV fields */ + tlv->tag = TLV_TYPE_2040BSS_COEXISTENCE; + tlv->length = + sizeof(tlvbuf_2040BSSCo_t) - TLVHEADER_LEN; + tlv->bss_co_2040.bss_co_2040_value = + (t_u8)A2HEXDECIMAL(args[1]); + endian_convert_tlv_header_out(tlv); + } else if (strcmp(args[0], "RSNInfo") == 0) { + /* Append a new TLV */ + rsn_ie = (tlvbuf_RsnParamSet_t *)(buffer + cmd_len); + tlv_len_rsn = sizeof(tlvbuf_RsnParamSet_t); + rsn_ie->tag = TLV_TYPE_RSN_IE; + rsn_ie->version = VERSION_RSN_IE; + rsn_ie->version = cpu_to_le16(rsn_ie->version); + cmd_len += tlv_len_rsn; + } else if (strcmp(args[0], "GroupCipherSuite") == 0) { + for (i = 0; i < GROUP_CIPHER_SUITE_LEN; i++) + rsn_ie->group_cipher_suite[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + } else if (strcmp(args[0], "PairwiseCipherCount") == 0) { + rsn_ie->pairwise_cipher_count = (t_u16)atoi(args[1]); + rsn_ie->pairwise_cipher_count = + cpu_to_le16(rsn_ie->pairwise_cipher_count); + } else if (strncmp(args[0], "PairwiseCipherSuite", 19) == 0) { + if (pairwise_index > MAX_PAIRWISE_CIPHER_SUITE_COUNT) { + printf("PairwiseCipherSuite exceeds max count\n"); + goto done; + } + tlv_len_rsn += PAIRWISE_CIPHER_SUITE_LEN; + cmd_len += PAIRWISE_CIPHER_SUITE_LEN; + for (i = 0; i < PAIRWISE_CIPHER_SUITE_LEN; i++) { + pairwise_cipher_suite[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + } + memcpy((t_u8 *)(rsn_ie->pairwise_cipher_suite + + (pairwise_index * + PAIRWISE_CIPHER_SUITE_LEN)), + pairwise_cipher_suite, + PAIRWISE_CIPHER_SUITE_LEN); + pairwise_index++; + pairwise_offset = + pairwise_index * PAIRWISE_CIPHER_SUITE_LEN; + } else if (strcmp(args[0], "AKMSuiteCount") == 0) { + akm_count = (t_u16)atoi(args[1]); + akm_count = cpu_to_le16(akm_count); + memcpy((((t_u8 *)(&rsn_ie->akm_suite_count)) + + pairwise_offset), &akm_count, sizeof(t_u16)); + } else if (strncmp(args[0], "AKMSuite", 8) == 0) { + if (akm_index > MAX_AKM_SUITE_COUNT) { + printf("AKMSuite exceeds max count\n"); + goto done; + } + tlv_len_rsn += AKM_SUITE_LEN; + cmd_len += AKM_SUITE_LEN; + for (i = 0; i < AKM_SUITE_LEN; i++) { + akm_suite[i] = (t_u8)A2HEXDECIMAL(args[i + 1]); + } + memcpy((t_u8 *)(rsn_ie->akm_suite + + (akm_index * AKM_SUITE_LEN) + + pairwise_offset), akm_suite, + AKM_SUITE_LEN); + akm_index++; + akm_offset = akm_index * AKM_SUITE_LEN; + } else if (strcmp(args[0], "RSNCapability") == 0) { + rsn_cap = (t_u16)A2HEXDECIMAL(args[1]); + rsn_cap = cpu_to_le16(rsn_cap); + memcpy(((t_u8 *)(&(rsn_ie->rsn_capability))) + + pairwise_offset + akm_offset, &rsn_cap, + sizeof(t_u16)); + } else if (strcmp(args[0], "PMKIDCount") == 0) { + pmk_count = (t_u16)atoi(args[1]); + pmk_count = cpu_to_le16(pmk_count); + memcpy((((t_u8 *)(&rsn_ie->pmkid_count)) + + pairwise_offset + akm_offset), &pmk_count, + sizeof(t_u16)); + rsn_ie->length = tlv_len_rsn - TLVHEADER_LEN; + endian_convert_tlv_header_out(rsn_ie); + } else if (strncmp(args[0], "PMKIDList", 9) == 0) { + if (pmkid_index > MAX_PMKID_COUNT) { + printf("PMKIDSuite exceeds max count\n"); + goto done; + } + for (i = 0; i < PMKID_LEN; i++) + pmkid[i] = (t_u8)A2HEXDECIMAL(args[i + 1]); + memcpy((t_u8 *)(rsn_ie->pmkid_list + + (pmkid_index * PMKID_LEN) + + pairwise_offset + akm_offset), pmkid, + PMKID_LEN); + pmkid_index++; + tlv_len_rsn += PMKID_LEN; + cmd_len += PMKID_LEN; + /* undo conversion done in PMKIDCount */ + endian_convert_tlv_header_in(rsn_ie); + rsn_ie->length = tlv_len_rsn - TLVHEADER_LEN; + endian_convert_tlv_header_out(rsn_ie); + } else if (strcmp(args[0], "SupportedChannels") == 0) { + /* Append a new TLV */ + supp_chan = + (tlvbuf_SupportedChannels_t *)(buffer + + cmd_len); + supp_chan->tag = TLV_TYPE_SUPPORTED_CHANNELS; + supp_chan->length = sizeof(tlvbuf_SupportedChannels_t) + - TLVHEADER_LEN; + tlv_len_supp_chan = sizeof(tlvbuf_SupportedChannels_t); + cmd_len += tlv_len_supp_chan; + } else if (strncmp(args[0], "FirstChannelNo", 14) == 0) { + supp_chan->subband[no_of_supp_chan_sub_band]. + start_chan = atoi(args[1]); + } else if (strcmp(args[0], "NumberofSubBandChannels") == 0) { + supp_chan->subband[no_of_supp_chan_sub_band].num_chans = + atoi(args[1]); + no_of_supp_chan_sub_band++; + tlv_len_supp_chan += + sizeof(IEEEtypes_SupportChan_Subband_t); + supp_chan->length += + sizeof(IEEEtypes_SupportChan_Subband_t); + cmd_len += sizeof(IEEEtypes_SupportChan_Subband_t); + endian_convert_tlv_header_out(supp_chan); + } else if (strcmp(args[0], "SupportedRegulatoryClasses") == 0) { + /* Append a new TLV */ + reg_class = + (tlvbuf_RegulatoryClass_t *)(buffer + cmd_len); + tlv_len_reg_class = sizeof(tlvbuf_RegulatoryClass_t); + reg_class->tag = TLV_TYPE_REGULATORY_CLASSES; + cmd_len += tlv_len_reg_class; + } else if (strcmp(args[0], "CurrentRegulatoryClass") == 0) { + reg_class->regulatory_class.cur_regulatory_class = + atoi(args[1]); + reg_class->length = 1; + } else if (strcmp(args[0], "NumofRegulatoryClasses") == 0) { + num_of_regulatory_class = atoi(args[1]); + reg_class->length += num_of_regulatory_class; + cmd_len += num_of_regulatory_class; + endian_convert_tlv_header_out(reg_class); + } else if (strcmp(args[0], "ListOfRegulatoryClasses") == 0) { + for (i = 0; i < num_of_regulatory_class; i++) + reg_class->regulatory_class. + regulatory_classes_list[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + } else if (strcmp(args[0], "CountryInfo") == 0) { + /* Append a new TLV */ + domain = (tlvbuf_DomainParamSet_t *)(buffer + cmd_len); + domain->tag = TLV_TYPE_DOMAIN; + domain->length = sizeof(tlvbuf_DomainParamSet_t) + - TLVHEADER_LEN; + tlv_len_domain = sizeof(tlvbuf_DomainParamSet_t); + cmd_len += tlv_len_domain; + } else if (strcmp(args[0], "CountryString") == 0) { + strncpy(country, args[1] + 1, strlen(args[1]) - 2); + country[strlen(args[1]) - 2] = '\0'; + for (i = 1; (unsigned int)i < strlen(country) - 2; i++) { + if ((country[i] < 'A') || (country[i] > 'z')) { + printf("Invalid Country Code\n"); + goto done; + } + if (country[i] > 'Z') + country[i] = country[i] - 'a' + 'A'; + } + memset(domain->country_code, ' ', + sizeof(domain->country_code)); + memcpy(domain->country_code, country, strlen(country)); + } else if (strncmp(args[0], "FirstChannel", 12) == 0) { + domain->sub_band[no_of_sub_band].first_chan = + atoi(args[1]); + } else if (strncmp(args[0], "NumberofChannels", 16) == 0) { + domain->sub_band[no_of_sub_band].no_of_chan = + atoi(args[1]); + } else if (strncmp(args[0], "TxPower", 7) == 0) { + domain->sub_band[no_of_sub_band].max_tx_pwr = + atoi(args[1]); + no_of_sub_band++; + domain->length += sizeof(IEEEtypes_SubbandSet_t); + tlv_len_domain += sizeof(IEEEtypes_SubbandSet_t); + cmd_len += sizeof(IEEEtypes_SubbandSet_t); + endian_convert_tlv_header_out(domain); + } else if (strcmp(args[0], "VHTCapability") == 0) { + /* Append a new TLV */ + tlv_vht_cap = (tlvbuf_VHTCap_t *)(buffer + cmd_len); + tlv_len = sizeof(tlvbuf_VHTCap_t); + tlv_vht_cap->tag = TLV_TYPE_VHT_CAP; + tlv_vht_cap->length = + sizeof(tlvbuf_VHTCap_t) - TLVHEADER_LEN; + cmd_len += tlv_len; + endian_convert_tlv_header_out(tlv_vht_cap); + } else if (strcmp(args[0], "VHTCapabilityInfo") == 0) { + tlv_vht_cap->vht_cap.vht_cap_info = + (t_u32)A2HEXDECIMAL(args[1]); + tlv_vht_cap->vht_cap.vht_cap_info = + cpu_to_le16(tlv_vht_cap->vht_cap.vht_cap_info); + } else if (strcmp(args[0], "RxMCSMap") == 0) { + tlv_vht_cap->vht_cap.mcs_sets.rx_mcs_map = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_vht_cap->vht_cap.mcs_sets.rx_mcs_map = + cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets. + rx_mcs_map); + } else if (strcmp(args[0], "TxMCSMap") == 0) { + tlv_vht_cap->vht_cap.mcs_sets.tx_mcs_map = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_vht_cap->vht_cap.mcs_sets.tx_mcs_map = + cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets. + tx_mcs_map); + } else if (strcmp(args[0], "RxMaxRate") == 0) { + tlv_vht_cap->vht_cap.mcs_sets.rx_max_rate = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_vht_cap->vht_cap.mcs_sets.rx_max_rate = + cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets. + rx_max_rate); + } else if (strcmp(args[0], "TxMaxRate") == 0) { + tlv_vht_cap->vht_cap.mcs_sets.tx_max_rate = + (t_u16)A2HEXDECIMAL(args[1]); + tlv_vht_cap->vht_cap.mcs_sets.tx_max_rate = + cpu_to_le16(tlv_vht_cap->vht_cap.mcs_sets. + tx_max_rate); + } else if (strcmp(args[0], "VHTOper") == 0) { + /* Append a new TLV */ + tlv_vht_oper = (tlvbuf_VHTOpra_t *)(buffer + cmd_len); + tlv_len = sizeof(tlvbuf_VHTOpra_t); + tlv_vht_oper->tag = TLV_TYPE_VHT_OPER; + tlv_vht_oper->length = + sizeof(tlvbuf_VHTOpra_t) - TLVHEADER_LEN; + cmd_len += tlv_len; + endian_convert_tlv_header_out(tlv_vht_oper); + } else if (strcmp(args[0], "ChanWidth") == 0) { + tlv_vht_oper->chan_width = A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "ChanCF1") == 0) { + tlv_vht_oper->chan_cf1 = A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "ChanCF2") == 0) { + tlv_vht_oper->chan_cf2 = (t_u16)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "BasicMCSMap") == 0) { + if ((arg_num - 1) != VHT_MCS_MAP_LEN) { + printf("Incorrect number of arguments for BasicMCSMap.\n"); + goto done; + } + for (i = 0; i < VHT_MCS_MAP_LEN; i++) + tlv_vht_oper->basic_mcs_map[i] = + (t_u8)A2HEXDECIMAL(args[i + 1]); + } + } + /* adjust for size of action and tlv_len, capInfo */ + param_buf->tlv_len = cmd_len - sizeof(tdls_setinfo); + + hexdump("tdls_setinfo", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS Info settings sucessfully set.\n"); + } else { + printf("ERR:Could not set TDLS info configuration.\n"); + } + +done: + if (config_file) + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_discovery + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_discovery(int argc, char *argv[]) +{ + tdls_discovery *param_buf = NULL; + tdls_discovery_resp *resp_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, ret = 0, cmd_found = 0, rssi = 0; + char *args[30], *pos = NULL, mac_addr[20]; + t_u8 peer_mac[ETH_ALEN]; + t_u16 cmd_len = 0, buf_len = 0, resp_len = 0; + t_u8 *buffer = NULL, *raw = NULL; + IEEEtypes_Header_t *tlv = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_discovery <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_discovery); + buf_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + buffer = (t_u8 *)malloc(buf_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, buf_len); + param_buf = (tdls_discovery *)buffer; + param_buf->action = ACTION_TDLS_DISCOVERY; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + return MLAN_STATUS_FAILURE; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + if (strcmp(args[0], "PeerMAC") == 0) { + strncpy(mac_addr, args[1], 20); + if ((ret = + mac2raw(mac_addr, + peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret + == + MAC_BROADCAST ? "Broadcast" : + "Multicast"); + goto done; + } + memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN); + } else if (strcmp(args[0], "}") == 0 && cmd_found) { + break; + } + } + hexdump("tdls_discovery", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + hexdump("tdls_response", buffer, 0x40, ' '); + printf("TDLS discovery done.\n"); + resp_buf = (tdls_discovery_resp *)buffer; + resp_len = resp_buf->payload_len; + printf("Response Length = %d\n", resp_len); + if (resp_len > 0) { + /* MAC */ + raw = resp_buf->peer_mac; + printf("\tPeer - %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned int)raw[0], (unsigned int)raw[1], + (unsigned int)raw[2], (unsigned int)raw[3], + (unsigned int)raw[4], (unsigned int)raw[5]); + + /* RSSI, CapInfo */ + rssi = (int)(resp_buf->rssi); + if (rssi > 0x7f) + rssi = -(256 - rssi); + printf("\tRssi : %d dBm\n", rssi); + printf("\tCapInfo = 0x%02X\n", resp_buf->cap_info); + + resp_len -= ETH_ALEN + sizeof(resp_buf->rssi) + + sizeof(resp_buf->cap_info); + + /* TLVs */ + tlv = (IEEEtypes_Header_t *)&resp_buf->tlv_buffer; + while (resp_len > IEEE_HEADER_LEN) { + switch (tlv->element_id) { + case TLV_TYPE_RATES: + printf("\tRates : "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + case TLV_EXTENDED_SUPPORTED_RATES: + printf("\tExtended Rates : "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + case TLV_TYPE_QOSINFO: + printf("\tQosInfo "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + + case TLV_TYPE_EXTCAP: + printf("\tExtended Cap "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + + case TLV_TYPE_HT_CAP: + printf("\tHT Cap "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + + case TLV_TYPE_HT_INFO: + printf("\tHT Info"); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + + case TLV_TYPE_2040BSS_COEXISTENCE: + printf("\t2040 BSS Coex "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + + case TLV_TYPE_RSN_IE: + printf("\tRSN IE "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + + case TLV_TYPE_SUPPORTED_CHANNELS: + printf("\tSupported Channels "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + + case TLV_TYPE_DOMAIN: + printf("\tDomain Info "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + case TLV_LINK_IDENTIFIER: + printf("\tLink identifier : "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + case TLV_TIMEOUT_INTERVAL: + printf("\tTimeout interval : "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + case TLV_TYPE_REGULATORY_CLASSES: + printf("\t Regulatory classes : "); + hexdump(NULL, + ((t_u8 *)tlv) + IEEE_HEADER_LEN, + tlv->len, ' '); + break; + default: + printf("Unknown TLV\n"); + hexdump(NULL, ((t_u8 *)tlv), + IEEE_HEADER_LEN + tlv->len, + ' '); + break; + } + resp_len -= tlv->len + IEEE_HEADER_LEN; + tlv = (IEEEtypes_Header_t *)((t_u8 *)tlv + + tlv->len + + IEEE_HEADER_LEN); + } + } + + } else { + printf("ERR:Command response = Fail!\n"); + } +done: + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_setup + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_setup(int argc, char *argv[]) +{ + tdls_setup *param_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, ret = 0, cmd_found = 0; + char *args[30], *pos = NULL, mac_addr[20]; + t_u8 peer_mac[ETH_ALEN]; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_setup <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_setup); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_setup *) buffer; + param_buf->action = ACTION_TDLS_SETUP; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + return MLAN_STATUS_FAILURE; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + + if (strcmp(args[0], "PeerMAC") == 0) { + strncpy(mac_addr, args[1], 20); + if ((ret = + mac2raw(mac_addr, + peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret + == + MAC_BROADCAST ? "Broadcast" : + "Multicast"); + goto done; + } + memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN); + } else if (strcmp(args[0], "WaitTimems") == 0) { + param_buf->wait_time = (t_u32)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "KeyLifetime") == 0) { + param_buf->key_life_time = (t_u32)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "}") == 0 && cmd_found) { + break; + } + } + hexdump("tdls_setup", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS setup request successful.\n"); + } else { + printf("ERR:TDLS setup request failed.\n"); + } + +done: + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_teardown + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_teardown(int argc, char *argv[]) +{ + tdls_teardown *param_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, ret = 0, cmd_found = 0; + char *args[30], *pos = NULL, mac_addr[20]; + t_u8 peer_mac[ETH_ALEN]; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_teardown <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_teardown); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_teardown *)buffer; + param_buf->action = ACTION_TDLS_TEARDOWN; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + return MLAN_STATUS_FAILURE; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + + if (strcmp(args[0], "PeerMAC") == 0) { + strncpy(mac_addr, args[1], 20); + if ((ret = + mac2raw(mac_addr, + peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret + == + MAC_BROADCAST ? "Broadcast" : + "Multicast"); + goto done; + } + memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN); + } else if (strcmp(args[0], "ReasonCode") == 0) { + param_buf->reason_code = (t_u16)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "}") == 0 && cmd_found) { + break; + } + } + hexdump("tdls_teardown", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS teardown request successful.\n"); + } else { + printf("ERR:TDLS teardown request failed.\n"); + } + +done: + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_powermode + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_powermode(int argc, char *argv[]) +{ + tdls_powermode *param_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, ret = 0, cmd_found = 0; + char *args[30], *pos = NULL, mac_addr[20]; + t_u8 peer_mac[ETH_ALEN]; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_powermode <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_powermode); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_powermode *)buffer; + param_buf->action = ACTION_TDLS_POWER_MODE; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + return MLAN_STATUS_FAILURE; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + + if (strcmp(args[0], "PeerMAC") == 0) { + strncpy(mac_addr, args[1], 20); + if ((ret = + mac2raw(mac_addr, + peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret + == + MAC_BROADCAST ? "Broadcast" : + "Multicast"); + goto done; + } + memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN); + } else if (strcmp(args[0], "PowerMode") == 0) { + param_buf->power_mode = (t_u16)A2HEXDECIMAL(args[1]); + if (param_buf->power_mode > 1) { + printf("ERR: Incorrect PowerMode value %s\n", + args[1]); + goto done; + } + } else if (strcmp(args[0], "}") == 0 && cmd_found) { + break; + } + } + hexdump("tdls_powermode", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS powermode request successful.\n"); + } else { + printf("ERR:TDLS powermode request failed.\n"); + } + +done: + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_link_status + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_link_status(int argc, char *argv[]) +{ + int ret = 0; + tdls_link_status *param_buf = NULL; + tdls_link_status_resp *resp_buf = NULL; + t_u16 cmd_len = 0, buf_len = 0, resp_len = 0, curr_link_len = 0; + t_u8 no_of_links = 0, peer_mac[ETH_ALEN]; + t_u8 *buffer = NULL, *raw = NULL; + tdls_each_link_status *link_ptr = NULL; + + /* Check arguments */ + if (argc != 3 && argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_link_status [peer_mac_addr]\n"); + exit(1); + } + + cmd_len = sizeof(tdls_link_status); + buf_len = MRVDRV_SIZE_OF_CMD_BUFFER; + + buffer = (t_u8 *)malloc(buf_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, buf_len); + param_buf = (tdls_link_status *)buffer; + param_buf->action = ACTION_TDLS_LINK_STATUS; + + if (argc == 4) { + if ((ret = mac2raw(argv[3], peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret == + MAC_BROADCAST ? "Broadcast" : "Multicast"); + goto done; + } + if (memcmp(peer_mac, "\x00\x00\x00\x00\x00\x00", ETH_ALEN)) { + memcpy(buffer + cmd_len, peer_mac, ETH_ALEN); + cmd_len += ETH_ALEN; + } + } + + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + hexdump("tdls_response", buffer, 0x60, ' '); + printf("TDLS Link Status - .\n"); + resp_buf = (tdls_link_status_resp *)buffer; + resp_len = resp_buf->payload_len; + printf("Response Length = %d\n", resp_len); + no_of_links = resp_buf->active_links; + printf("No of active links = %d\n", no_of_links); + resp_len--; + link_ptr = resp_buf->link_stats; + while (resp_len > 0 && no_of_links > 0) { + curr_link_len = 0; + /* MAC */ + raw = link_ptr->peer_mac; + printf("\tPeer - %02x:%02x:%02x:%02x:%02x:%02x\n", + (unsigned int)raw[0], (unsigned int)raw[1], + (unsigned int)raw[2], (unsigned int)raw[3], + (unsigned int)raw[4], (unsigned int)raw[5]); + + printf("\t %s initiated link.\n", + (link_ptr->link_flags & 0x01) ? "Self" : "Peer"); + printf("\t Security %s.\n", + (link_ptr-> + link_flags & 0x02) ? "Enabled" : "Disabled"); + printf("\t Self PS status = %s.\n", + (link_ptr-> + link_flags & 0x04) ? "Sleep" : "Active"); + printf("\t Peer PS status = %s.\n", + (link_ptr-> + link_flags & 0x08) ? "Sleep" : "Active"); + printf("\t Channel switch is %ssupported\n", + (link_ptr->link_flags & 0x10) ? "" : "NOT "); + printf("\t Current Channel %s\n", + (link_ptr->link_flags & 0x20) ? "off" : "base"); + + if (link_ptr->traffic_status) { + printf("\t Buffered traffic for"); + printf("%s", + (link_ptr-> + traffic_status & 0x01) ? "AC_BK, " : + ""); + printf("%s", + (link_ptr-> + traffic_status & 0x02) ? "AC_BE, " : + ""); + printf("%s", + (link_ptr-> + traffic_status & 0x04) ? "AC_VI, " : + ""); + printf("%s", + (link_ptr-> + traffic_status & 0x08) ? "AC_VO" : ""); + printf(".\n"); + } + printf("\t Successive Tx Failure count = %d\n", + link_ptr->tx_fail_count); + printf("\t Active channel number = %d\n", + link_ptr->active_channel); + printf("\t Last Data RSSI = %d dBm\n", + link_ptr->data_rssi_last); + printf("\t Last Data NF = %d dBm\n", + link_ptr->data_nf_last); + printf("\t Average Data RSSI = %d dBm\n", + link_ptr->data_rssi_avg); + printf("\t Average Data NF = %d dBm\n", + link_ptr->data_nf_avg); + printf("\t Tx data rate = %d Mbps\n", + link_ptr->u.final_data_rate); + + /* size of unsecure structure */ + curr_link_len = sizeof(tdls_each_link_status) - + (sizeof(t_u32) + sizeof(t_u8) + sizeof(t_u8)); + + if (link_ptr->link_flags & 0x02) { + /* security details */ + printf("\t Security Method = %s\n", + (link_ptr->security_method == + 1) ? "AES" : "None"); + printf("\t Key Lifetime = %d ms\n\t", + link_ptr->key_lifetime); + hexdump("Key", ((t_u8 *)link_ptr->key), + link_ptr->key_length, ' '); + curr_link_len += + sizeof(t_u32) + sizeof(t_u8) + + sizeof(t_u8) + + link_ptr->key_length; + } + resp_len -= curr_link_len; + link_ptr = + (tdls_each_link_status *)(((t_u8 *)link_ptr) + + curr_link_len); + printf(".\n"); + } + + } else { + printf("ERR:Command response = Fail!\n"); + } +done: + if (buffer) + free(buffer); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_channel_swtich + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_channel_switch(int argc, char *argv[]) +{ + + tdls_channel_switch *param_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, ret = 0, cmd_found = 0; + char *args[30], *pos = NULL, mac_addr[20]; + t_u8 peer_mac[ETH_ALEN]; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_channel_switch <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_channel_switch); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_channel_switch *)buffer; + param_buf->action = ACTION_TDLS_INIT_CHAN_SWITCH; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + return MLAN_STATUS_FAILURE; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + + if (strcmp(args[0], "PeerMAC") == 0) { + strncpy(mac_addr, args[1], 20); + if ((ret = + mac2raw(mac_addr, + peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret + == + MAC_BROADCAST ? "Broadcast" : + "Multicast"); + goto done; + } + memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN); + } else if (strcmp(args[0], "Band") == 0) { + param_buf->band = (t_u16)A2HEXDECIMAL(args[1]); + if (param_buf->band != BAND_BG && + param_buf->band != BAND_A) { + printf("ERR: Incorrect Band value %s\n", + args[1]); + goto done; + } + } else if (strcmp(args[0], "RegulatoryClass") == 0) { + param_buf->regulatory_class = + (t_u16)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "PrimaryChannel") == 0) { + param_buf->primary_channel = + (t_u16)A2HEXDECIMAL(args[1]); + if (param_buf->band == BAND_BG && + param_buf->primary_channel < MIN_BG_CHANNEL && + param_buf->primary_channel > MAX_BG_CHANNEL) { + printf("ERR: Incorrect Primary Channel value %s\n", args[1]); + goto done; + } else if (param_buf->band == BAND_A + && param_buf->primary_channel < MIN_A_CHANNEL + && param_buf->primary_channel > + MAX_A_CHANNEL) { + printf("ERR: Incorrect Primary Channel value %s\n", args[1]); + goto done; + } + } else if (strcmp(args[0], "SecondaryChannelOffset") == 0) { + param_buf->secondary_channel_offset = + (t_u16)A2HEXDECIMAL(args[1]); + if (param_buf->secondary_channel_offset != 0 && + param_buf->secondary_channel_offset != + SECOND_CHANNEL_ABOVE && + param_buf->secondary_channel_offset != + SECOND_CHANNEL_BELOW) { + printf("ERR: Incorrect Secondary Channel Offset value %s\n", args[1]); + goto done; + } + } else if (strcmp(args[0], "ChannelSwitchTime") == 0) { + param_buf->switch_time = (t_u16)A2HEXDECIMAL(args[1]); + if (param_buf->switch_time == 0) { + printf("ERR: Incorrect Channel Switch time %s\n", args[1]); + goto done; + } + } else if (strcmp(args[0], "ChannelSwitchTimeout") == 0) { + param_buf->switch_timeout = + (t_u16)A2HEXDECIMAL(args[1]); + if (param_buf->switch_timeout == 0) { + printf("ERR: Incorrect Channel Switch timeout %s\n", args[1]); + goto done; + } + } else if (strcmp(args[0], "Periodicity") == 0) { + param_buf->periodicity = (t_u16)A2HEXDECIMAL(args[1]); + if (param_buf->periodicity != NO_PERIODIC_SWITCH + && param_buf->periodicity != + ENABLE_PERIODIC_SWITCH) { + printf("ERR: Incorrect Periodicity value %s\n", + args[1]); + goto done; + } + } else if (strcmp(args[0], "}") == 0 && cmd_found) { + break; + } + } + hexdump("tdls_channel_switch", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS channel switch request successful.\n"); + } else { + printf("ERR:TDLS channel switch request failed.\n"); + } + +done: + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief disable tdls_channel_swtich + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_disable_channel_switch(int argc, char *argv[]) +{ + + tdls_disable_cs *param_buf = NULL; + int ret = 0; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_disable_cs <0/1>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_disable_cs); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_disable_cs *) buffer; + param_buf->action = ACTION_TDLS_CS_DISABLE; + + param_buf->data = (t_u16)A2HEXDECIMAL(argv[3]); + if ((param_buf->data != 0) && (param_buf->data != 1)) { + printf("ERR:Incorrect arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_disable_cs <0/1>\n"); + goto done; + } + hexdump("tdls_disable_cs", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS disable channel switch successful.\n"); + } else { + printf("ERR:TDLS disable channel switch failed.\n"); + } + +done: + if (buffer) + free(buffer); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_stop_channel_switch + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_stop_channel_switch(int argc, char *argv[]) +{ + tdls_stop_chan_switch *param_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, ret = 0, cmd_found = 0; + char *args[30], *pos = NULL, mac_addr[20]; + t_u8 peer_mac[ETH_ALEN]; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_stop_channel_switch <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_stop_chan_switch); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_stop_chan_switch *) buffer; + param_buf->action = ACTION_TDLS_STOP_CHAN_SWITCH; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + return MLAN_STATUS_FAILURE; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + + if (strcmp(args[0], "PeerMAC") == 0) { + strncpy(mac_addr, args[1], 20); + if ((ret = + mac2raw(mac_addr, + peer_mac)) != MLAN_STATUS_SUCCESS) { + printf("ERR: %s Address \n", + ret == + MLAN_STATUS_FAILURE ? "Invalid MAC" : ret + == + MAC_BROADCAST ? "Broadcast" : + "Multicast"); + goto done; + } + memcpy(param_buf->peer_mac, peer_mac, ETH_ALEN); + } else if (strcmp(args[0], "}") == 0 && cmd_found) { + break; + } + } + hexdump("tdls_stop_channel_switch", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS stop channel switch successful.\n"); + } else { + printf("ERR:TDLS stop channel switch failed.\n"); + } + +done: + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_cs_params + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_cs_params(int argc, char *argv[]) +{ + tdls_cs_params *param_buf = NULL; + char *line = NULL; + FILE *config_file = NULL; + int li = 0, ret = 0, cmd_found = 0; + char *args[30], *pos = NULL; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + + /* Check arguments */ + if (argc != 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_cs_params <config/tdls.conf>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_cs_params); + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + goto done; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_cs_params *) buffer; + param_buf->action = ACTION_TDLS_CS_PARAMS; + + /* Check if file exists */ + config_file = fopen(argv[3], "r"); + if (config_file == NULL) { + printf("\nERR:Config file can not open.\n"); + return MLAN_STATUS_FAILURE; + } + line = (char *)malloc(MAX_CONFIG_LINE); + if (!line) { + printf("ERR:Cannot allocate memory for line\n"); + goto done; + } + memset(line, 0, MAX_CONFIG_LINE); + + /* Parse file and process */ + while (config_get_line(line, MAX_CONFIG_LINE, config_file, &li, &pos)) { + if (!cmd_found && strncmp(args[0], argv[2], strlen(args[0]))) + continue; + + cmd_found = 1; + + if (strcmp(args[0], "UnitTime") == 0) { + param_buf->unit_time = (t_u8)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "ThresholdOtherLink") == 0) { + param_buf->threshold_otherlink = + (t_u8)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "ThresholdDirectLink") == 0) { + param_buf->threshold_directlink = + (t_u8)A2HEXDECIMAL(args[1]); + } else if (strcmp(args[0], "}") == 0 && cmd_found) { + break; + } + } + hexdump("tdls_cs_params", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS set channel switch parameters successful.\n"); + } else { + printf("ERR:TDLS set channel switch parameters failed.\n"); + } +done: + fclose(config_file); + if (buffer) + free(buffer); + if (line) + free(line); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process tdls_debug + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +process_tdls_debug(int argc, char *argv[]) +{ + int ret = 0; + tdls_debug *param_buf = NULL; + t_u16 cmd_len = 0; + t_u8 *buffer = NULL; + t_u16 action = 0, value = 0; + + /* Check arguments */ + if (argc < 4) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug <options>\n"); + exit(1); + } + + cmd_len = sizeof(tdls_debug); + + /* wrong_bss */ + if (!strcmp(argv[3], "wrong_bss")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_WRONG_BSS; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug wrong_bss <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* same link */ + else if (!strcmp(argv[3], "setup_existing_link")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_SETUP_SAME_LINK; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug setup_existing_link <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* fail_setup_confirm */ + else if (!strcmp(argv[3], "fail_setup_confirm")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_FAIL_SETUP_CONFIRM; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug fail_setup_confirm <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* setup prohibited */ + else if (!strcmp(argv[3], "setup_with_prohibited")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_SETUP_PROHIBITED; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug setup_with_prohibited <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* setup higher/lower mac */ + else if (!strcmp(argv[3], "higher_lower_mac")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_HIGHER_LOWER_MAC; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug higher_lower_mac <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* ignore key lifetime expiry */ + else if (!strcmp(argv[3], "ignore_key_expiry")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_IGNORE_KEY_EXPIRY; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug ignore_key_expiry <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* allow weak security */ + else if (!strcmp(argv[3], "allow_weak_security")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_ALLOW_WEAK_SECURITY; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug allow_weak_security <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* stop RX */ + else if (!strcmp(argv[3], "stop_rx")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_STOP_RX; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug stop_rx <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } + /* Immediate return */ + else if (!strcmp(argv[3], "cs_im_return")) { + cmd_len += sizeof(t_u16); + action = ACTION_TDLS_DEBUG_CS_RET_IM; + if (argc < 5) { + printf("ERR:Incorrect number of arguments.\n"); + printf("Syntax: ./mlanconfig mlanX tdls_debug cs_im_return <0/1>\n"); + exit(1); + } + value = (t_u16)A2HEXDECIMAL(argv[4]); + } else { + printf("ERR:Incorrect command!\n"); + exit(1); + } + + buffer = (t_u8 *)malloc(cmd_len); + if (!buffer) { + printf("ERR:Cannot allocate memory!\n"); + return -1; + } + memset(buffer, 0, cmd_len); + param_buf = (tdls_debug *)buffer; + param_buf->action = action; + memcpy(param_buf->data, &value, sizeof(value)); + + hexdump("tdls_debug", buffer, cmd_len, ' '); + /* Send collective command */ + ret = tdls_ioctl((t_u8 *)buffer, cmd_len); + + /* Process response */ + if (ret == MLAN_STATUS_SUCCESS) { + printf("TDLS debug request successful.\n"); + } else { + printf("ERR:TDLS debug request failed.\n"); + } + + if (buffer) + free(buffer); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Entry function for mlanconfig + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +main(int argc, char *argv[]) +{ + t_s32 cmd; + + if ((argc == 2) && (strcmp(argv[1], "-v") == 0)) { + fprintf(stdout, "Marvell mlanconfig version %s\n", + MLANCONFIG_VER); + exit(0); + } + if (argc < 3) { + fprintf(stderr, "Invalid number of parameters!\n"); + display_usage(); + exit(1); + } + + strncpy(dev_name, argv[1], IFNAMSIZ - 1); + + /* + * create a socket + */ + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + fprintf(stderr, "mlanconfig: Cannot open socket.\n"); + exit(1); + } + if (get_range() < 0) { + fprintf(stderr, "mlanconfig: Cannot get range.\n"); + close(sockfd); + exit(1); + } + switch ((cmd = findcommand(NELEMENTS(commands), commands, argv[2]))) { + case CMD_HOSTCMD: + process_host_cmd(argc, argv); + break; + case CMD_MEFCFG: + process_mef_cfg(argc, argv); + break; + case CMD_ARPFILTER: + process_arpfilter(argc, argv); + break; + case CMD_CFG_DATA: + process_cfg_data(argc, argv); + break; + case CMD_CMD52RW: + process_sdcmd52rw(argc, argv); + break; + case CMD_CMD53RW: + process_sdcmd53rw(argc, argv); + break; + case CMD_GET_SCAN_RSP: + process_getscantable(argc, argv); + break; + case CMD_SET_USER_SCAN: + process_setuserscan(argc, argv); + break; + case CMD_ADD_TS: + process_addts(argc, argv); + break; + case CMD_DEL_TS: + process_delts(argc, argv); + break; + case CMD_QCONFIG: + process_qconfig(argc, argv); + break; + case CMD_QSTATS: + process_qstats(argc, argv); + break; + case CMD_TS_STATUS: + process_wmm_ts_status(argc, argv); + break; + case CMD_WMM_QSTATUS: + process_wmm_qstatus(argc, argv); + break; + case CMD_REGRW: + process_regrdwr(argc, argv); + break; + case CMD_MEMRW: + process_memrdwr(argc, argv); + break; + case CMD_STA_CUSTOM_IE: + process_custom_ie(argc, argv); + break; + case CMD_STA_MGMT_FRAME_TX: + process_mgmt_frame_tx(argc, argv); + break; + case CMD_TDLS_CONF: + process_tdls_config(argc, argv); + break; + case CMD_TDLS_INFO: + process_tdls_setinfo(argc, argv); + break; + case CMD_TDLS_DISCOVERY: + process_tdls_discovery(argc, argv); + break; + case CMD_TDLS_SETUP: + process_tdls_setup(argc, argv); + break; + case CMD_TDLS_TEARDOWN: + process_tdls_teardown(argc, argv); + break; + case CMD_TDLS_POWERMODE: + process_tdls_powermode(argc, argv); + break; + case CMD_TDLS_LINK_STATUS: + process_tdls_link_status(argc, argv); + break; + case CMD_TDLS_CHANNEL_SWITCH: + process_tdls_channel_switch(argc, argv); + break; + case CMD_TDLS_STOP_CHAN_SWITCH: + process_tdls_stop_channel_switch(argc, argv); + break; + case CMD_TDLS_CS_PARAMS: + process_tdls_cs_params(argc, argv); + break; + case CMD_TDLS_CS_DISABLE: + process_tdls_disable_channel_switch(argc, argv); + break; + case CMD_TDLS_DEBUG: + process_tdls_debug(argc, argv); + break; + default: + fprintf(stderr, "Invalid command specified!\n"); + display_usage(); + close(sockfd); + exit(1); + } + + close(sockfd); + return MLAN_STATUS_SUCCESS; +}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanconfig.h b/wlan_sd8897/mapp/mlanconfig/mlanconfig.h new file mode 100644 index 0000000..3d1508c --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/mlanconfig.h
@@ -0,0 +1,791 @@ +/** @file mlanconfig.h + * + * @brief This file contains definitions for application + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 11/26/2008: initial version +************************************************************************/ +#ifndef _MLANCONFIG_H_ +#define _MLANCONFIG_H_ + +/** Include header files */ +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <linux/if.h> +#include <linux/wireless.h> +#include <sys/types.h> +#include <linux/if_ether.h> +#include <time.h> + +#if (BYTE_ORDER == LITTLE_ENDIAN) +#undef BIG_ENDIAN_SUPPORT +#endif + +/** Type definition: boolean */ +typedef enum { FALSE, TRUE } boolean; + +/** + * This macro specifies the attribute pack used for structure packing + */ +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__((packed)) +#endif + +/** 16 bits byte swap */ +#define swap_byte_16(x) \ +((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \ + (((t_u16)(x) & 0xff00U) >> 8))) + +/** 32 bits byte swap */ +#define swap_byte_32(x) \ +((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \ + (((t_u32)(x) & 0x0000ff00UL) << 8) | \ + (((t_u32)(x) & 0x00ff0000UL) >> 8) | \ + (((t_u32)(x) & 0xff000000UL) >> 24))) + +/** Convert to correct endian format */ +#ifdef BIG_ENDIAN_SUPPORT +/** CPU to little-endian convert for 16-bit */ +#define cpu_to_le16(x) swap_byte_16(x) +/** CPU to little-endian convert for 32-bit */ +#define cpu_to_le32(x) swap_byte_32(x) +/** Little-endian to CPU convert for 16-bit */ +#define le16_to_cpu(x) swap_byte_16(x) +/** Little-endian to CPU convert for 32-bit */ +#define le32_to_cpu(x) swap_byte_32(x) +#else +/** Do nothing */ +#define cpu_to_le16(x) (x) +/** Do nothing */ +#define cpu_to_le32(x) (x) +/** Do nothing */ +#define le16_to_cpu(x) (x) +/** Do nothing */ +#define le32_to_cpu(x) (x) +#endif + +/** Character, 1 byte */ +typedef char t_s8; +/** Unsigned character, 1 byte */ +typedef unsigned char t_u8; + +/** Short integer */ +typedef signed short t_s16; +/** Unsigned short integer */ +typedef unsigned short t_u16; + +/** Integer */ +typedef signed int t_s32; +/** Unsigned integer */ +typedef unsigned int t_u32; + +/** Long long integer */ +typedef signed long long t_s64; +/** Unsigned long long integer */ +typedef unsigned long long t_u64; + +/** Void pointer (4-bytes) */ +typedef void t_void; + +/** Success */ +#define MLAN_STATUS_SUCCESS (0) +/** Failure */ +#define MLAN_STATUS_FAILURE (-1) + +t_s8 *mlan_config_get_line(FILE * fp, t_s8 *s, t_s32 size, int *line); +int get_priv_ioctl(char *ioctl_name, int *ioctl_val, int *subioctl_val); +int fparse_for_hex(FILE * fp, t_u8 *dst); + +/** + * Hex or Decimal to Integer + * @param num string to convert into decimal or hex + */ +#define A2HEXDECIMAL(num) \ + (strncasecmp("0x", (num), 2)?(unsigned int) strtoll((num),NULL,0):a2hex((num))) + +/** Convert character to integer */ +#define CHAR2INT(x) (((x) >= 'A') ? ((x) - 'A' + 10) : ((x) - '0')) + +/** Convert TLV header from little endian format to CPU format */ +#define endian_convert_tlv_header_in(x) \ + { \ + (x)->tag = le16_to_cpu((x)->tag); \ + (x)->length = le16_to_cpu((x)->length); \ + } + +/** Convert TLV header to little endian format from CPU format */ +#define endian_convert_tlv_header_out(x) \ + { \ + (x)->tag = cpu_to_le16((x)->tag); \ + (x)->length = cpu_to_le16((x)->length); \ + } +/** Private command ID to pass custom IE list */ +#define CUSTOM_IE_CFG (SIOCDEVPRIVATE + 13) +/* TLV Definitions */ +/** TLV header */ +#define TLVHEADER /** Tag */ \ + t_u16 tag; \ + /** Length */ \ + t_u16 length + +/** Maximum IE buffer length */ +#define MAX_IE_BUFFER_LEN 256 + +/** TLV: Management IE list */ +#define MRVL_MGMT_IE_LIST_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0x69) //0x0169 + +/** TLV: Max Management IE */ +#define MRVL_MAX_MGMT_IE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 0xaa) //0x01aa + +/** custom IE info */ +typedef struct _custom_ie_info { + /** size of buffer */ + t_u16 buf_size; + /** no of buffers of buf_size */ + t_u16 buf_count; +} __ATTRIB_PACK__ custom_ie_info; + +/** TLV buffer : custom IE */ +typedef struct _tlvbuf_max_mgmt_ie { + /** Header */ + TLVHEADER; + /** No of tuples */ + t_u16 count; + /** custom IE info tuples */ + custom_ie_info info[0]; +} __ATTRIB_PACK__ tlvbuf_max_mgmt_ie; + +/** custom IE */ +typedef struct _custom_ie { + /** IE Index */ + t_u16 ie_index; + /** Mgmt Subtype Mask */ + t_u16 mgmt_subtype_mask; + /** IE Length */ + t_u16 ie_length; + /** IE buffer */ + t_u8 ie_buffer[0]; +} __ATTRIB_PACK__ custom_ie; + +/** TLV buffer : custom IE */ +typedef struct _tlvbuf_custom_ie { + /** Header */ + TLVHEADER; + /** custom IE data */ + custom_ie ie_data[0]; +} __ATTRIB_PACK__ tlvbuf_custom_ie; + +/** Maximum length of lines in configuration file */ +#define MAX_CONFIG_LINE 1024 +/** Ethernet address length */ +#define ETH_ALEN 6 +/** MAC BROADCAST */ +#define MAC_BROADCAST 0x1FF +/** MAC MULTICAST */ +#define MAC_MULTICAST 0x1FE + +/** pkt_header */ +typedef struct _pkt_header { + /** pkt_len */ + t_u32 pkt_len; + /** pkt_type */ + t_u32 TxPktType; + /** tx control */ + t_u32 TxControl; +} pkt_header; + +/** wlan_802_11_header packet from FW with length */ +typedef struct _wlan_mgmt_frame_tx { + /** Packet Length */ + t_u16 frm_len; + /** Frame Control */ + t_u16 frm_ctl; + /** Duration ID */ + t_u16 duration_id; + /** Address1 */ + t_u8 addr1[ETH_ALEN]; + /** Address2 */ + t_u8 addr2[ETH_ALEN]; + /** Address3 */ + t_u8 addr3[ETH_ALEN]; + /** Sequence Control */ + t_u16 seq_ctl; + /** Address4 */ + t_u8 addr4[ETH_ALEN]; + /** Frame payload */ + t_u8 payload[0]; +} __ATTRIB_PACK__ wlan_mgmt_frame_tx; + +/** frame tx ioctl number */ +#define FRAME_TX_IOCTL (SIOCDEVPRIVATE + 12) + +/** band BG */ +#define BAND_BG 0 +/** band A */ +#define BAND_A 1 +/** secondary channel is above */ +#define SECOND_CHANNEL_ABOVE 0x1 +/** secondary channel is below */ +#define SECOND_CHANNEL_BELOW 0x3 +/** NO PERIODIC SWITCH */ +#define NO_PERIODIC_SWITCH 0 +/** Enable periodic channel switch */ +#define ENABLE_PERIODIC_SWITCH 1 +/** Min channel value for BG band */ +#define MIN_BG_CHANNEL 1 +/** Max channel value for BG band */ +#define MAX_BG_CHANNEL 14 +/** Max channel value for A band */ +#define MIN_A_CHANNEL 36 +/** Max channel value for A band */ +#define MAX_A_CHANNEL 252 + +/** Host Command ioctl number */ +#define TDLS_IOCTL (SIOCDEVPRIVATE + 5) +/** TDLS action definitions */ +/** Action ID for TDLS config */ +#define ACTION_TDLS_CONFIG 0x0000 +/** Action ID for TDLS setinfo request */ +#define ACTION_TDLS_SETINFO 0x0001 +/** Action ID for TDLS Discovery request */ +#define ACTION_TDLS_DISCOVERY 0x0002 +/** Action ID for TDLS setup request */ +#define ACTION_TDLS_SETUP 0x0003 +/** Action ID for TDLS Teardown request */ +#define ACTION_TDLS_TEARDOWN 0x0004 +/** Action ID for TDLS power mode */ +#define ACTION_TDLS_POWER_MODE 0x0005 +/**Action ID for init TDLS Channel Switch*/ +#define ACTION_TDLS_INIT_CHAN_SWITCH 0x06 +/** Action ID for stop TDLS Channel Switch */ +#define ACTION_TDLS_STOP_CHAN_SWITCH 0x07 +/** Action ID for configure CS related parameters */ +#define ACTION_TDLS_CS_PARAMS 0x08 +/** Action ID for TDLS Disable Channel switch */ +#define ACTION_TDLS_CS_DISABLE 0x09 +/** Action ID for TDLS Link status */ +#define ACTION_TDLS_LINK_STATUS 0x000A +/** Action ID for TDLS CS immediate return */ +#define ACTION_TDLS_DEBUG_CS_RET_IM 0xFFF7 +/** Action ID for TDLS Stop RX */ +#define ACTION_TDLS_DEBUG_STOP_RX 0xFFF8 +/** Action ID for TDLS Allow weak security */ +#define ACTION_TDLS_DEBUG_ALLOW_WEAK_SECURITY 0xFFF9 +/** Action ID for TDLS Ignore key lifetime expiry */ +#define ACTION_TDLS_DEBUG_IGNORE_KEY_EXPIRY 0xFFFA +/** Action ID for TDLS Higher/Lower mac Test */ +#define ACTION_TDLS_DEBUG_HIGHER_LOWER_MAC 0xFFFB +/** Action ID for TDLS Prohibited Test */ +#define ACTION_TDLS_DEBUG_SETUP_PROHIBITED 0xFFFC +/** Action ID for TDLS Existing link Test */ +#define ACTION_TDLS_DEBUG_SETUP_SAME_LINK 0xFFFD +/** Action ID for TDLS Fail Setup Confirm */ +#define ACTION_TDLS_DEBUG_FAIL_SETUP_CONFIRM 0xFFFE +/** Action ID for TDLS WRONG BSS Test */ +#define ACTION_TDLS_DEBUG_WRONG_BSS 0xFFFF + +/** TLV type : Rates */ +#define TLV_TYPE_RATES 0x0001 +/** TLV type : Domain */ +#define TLV_TYPE_DOMAIN 0x0007 +/** TLV type : Supported channels */ +#define TLV_TYPE_SUPPORTED_CHANNELS 0x0024 +/** TLV type : HT Capabilities */ +#define TLV_TYPE_HT_CAP 0x002d +/** TLV type : Qos Info */ +#define TLV_TYPE_QOSINFO 0x002e +/** TLV type : RSN IE */ +#define TLV_TYPE_RSN_IE 0x0030 +/** TLV type : extended supported rates */ +#define TLV_EXTENDED_SUPPORTED_RATES 0x0032 +/** TLV type : timeout interval */ +#define TLV_TIMEOUT_INTERVAL 0x0038 +/** TLV type : Regulatory classes */ +#define TLV_TYPE_REGULATORY_CLASSES 0x003b +/** TLV type : HT Information */ +#define TLV_TYPE_HT_INFO 0x003d +/** TLV type : 20/40 BSS Coexistence */ +#define TLV_TYPE_2040BSS_COEXISTENCE 0x0048 +/** TLv Type : Link identifier */ +#define TLV_LINK_IDENTIFIER 0x0065 +/** TLV type : Extended capabilities */ +#define TLV_TYPE_EXTCAP 0x007f + +/** TLV type : VHT capabilities */ +#define TLV_TYPE_VHT_CAP 0x00BF +/** TLV type : VHT operations */ +#define TLV_TYPE_VHT_OPER 0x00C0 +/** Length of Basic MCS MAP */ +#define VHT_MCS_MAP_LEN 2 + +/** Country code length */ +#define COUNTRY_CODE_LEN 3 +/** Length of Group Cipher Suite */ +#define GROUP_CIPHER_SUITE_LEN 4 +/** Length of Pairwise Cipher Suite */ +#define PAIRWISE_CIPHER_SUITE_LEN 4 +/** Length of AKM Suite */ +#define AKM_SUITE_LEN 4 +/** PMKID length */ +#define PMKID_LEN 16 +/** Maximum number of pairwise_cipher_suite */ +#define MAX_PAIRWISE_CIPHER_SUITE_COUNT 2 +/** Maximum number of AKM suite */ +#define MAX_AKM_SUITE_COUNT 2 +/** Maximum number of PMKID list count */ +#define MAX_PMKID_COUNT 2 +/** Length of MCS set */ +#define MCS_SET_LEN 16 +/** Version in RSN IE */ +#define VERSION_RSN_IE 0x0001 + +/** tdls setinfo */ +typedef struct _tdls_setinfo { + /** Action */ + t_u16 action; + /** (TLV + capInfo) length */ + t_u16 tlv_len; + /** Capability Info */ + t_u16 cap_info; + /** tdls info */ + t_u8 tlv_buffer[0]; +} __ATTRIB_PACK__ tdls_setinfo; + +/** tdls discovery */ +typedef struct _tdls_discovery { + /** Action */ + t_u16 action; + /** Peer MAC address */ + t_u8 peer_mac[ETH_ALEN]; +} __ATTRIB_PACK__ tdls_discovery; + +/** tdls link status */ +typedef struct _tdls_links_status { + /** Action */ + t_u16 action; +} __ATTRIB_PACK__ tdls_link_status; + +/** tdls discovery response */ +typedef struct _tdls_discovery_resp { + /** Action */ + t_u16 action; + /** payload length */ + t_u16 payload_len; + /** peer mac Address */ + t_u8 peer_mac[ETH_ALEN]; + /** RSSI */ + signed char rssi; + /** Cap Info */ + t_u16 cap_info; + /** TLV buffer */ + t_u8 tlv_buffer[0]; +} __ATTRIB_PACK__ tdls_discovery_resp; + +/** tdls each link rate information */ +typedef struct _tdls_link_rate_info { + /** Tx Data Rate */ + t_u8 tx_data_rate; + /** Tx Rate HT info*/ + t_u8 tx_rate_htinfo; +} __ATTRIB_PACK__ tdls_link_rate_info; + +/** tdls each link status */ +typedef struct _tdls_each_link_status { + /** peer mac Address */ + t_u8 peer_mac[ETH_ALEN]; + /** Link Flags */ + t_u8 link_flags; + /** Traffic Status */ + t_u8 traffic_status; + /** Tx Failure Count */ + t_u8 tx_fail_count; + /** Channel Number */ + t_u32 active_channel; + /** Last Data RSSI in dBm */ + t_s16 data_rssi_last; + /** Last Data NF in dBm */ + t_s16 data_nf_last; + /** AVG DATA RSSI in dBm */ + t_s16 data_rssi_avg; + /** AVG DATA NF in dBm */ + t_s16 data_nf_avg; + union { + /** tdls rate info */ + tdls_link_rate_info rate_info; + /** final rate value*/ + t_u16 final_data_rate; + } u; + /** Security Method */ + t_u8 security_method; + /** Key Lifetime */ + t_u32 key_lifetime; + /** Key Length */ + t_u8 key_length; + /** actual key */ + t_u8 key[0]; +} __ATTRIB_PACK__ tdls_each_link_status; + +/** tdls link status response */ +typedef struct _tdls_link_status_resp { + /** Action */ + t_u16 action; + /** payload length */ + t_u16 payload_len; + /** number of links */ + t_u8 active_links; + /** structure for link status */ + tdls_each_link_status link_stats[1]; +} __ATTRIB_PACK__ tdls_link_status_resp; + +/** tdls setup */ +typedef struct _tdls_setup { + /** Action */ + t_u16 action; + /** Peer MAC address */ + t_u8 peer_mac[ETH_ALEN]; + /** Time to wait for response from peer*/ + t_u32 wait_time; + /** Key Life Time */ + t_u32 key_life_time; +} __ATTRIB_PACK__ tdls_setup; + +/** tdls tear down */ +typedef struct _tdls_tear_down { + /** Action */ + t_u16 action; + /** Peer MAC address */ + t_u8 peer_mac[ETH_ALEN]; + /** Reason code */ + t_u16 reason_code; +} __ATTRIB_PACK__ tdls_teardown; + +/** tdls power mode */ +typedef struct _tdls_power_mode { + /** Action */ + t_u16 action; + /** Peer MAC address */ + t_u8 peer_mac[ETH_ALEN]; + /** Power mode */ + t_u16 power_mode; +} __ATTRIB_PACK__ tdls_powermode; + +/** tdls channel switch info */ +typedef struct _tdls_channel_switch { + /** Action */ + t_u16 action; + /** peer mac Address */ + t_u8 peer_mac[ETH_ALEN]; + /** Channel Switch primary channel no */ + t_u8 primary_channel; + /** Channel Switch secondary channel offset */ + t_u8 secondary_channel_offset; + /** Channel Switch Band */ + t_u8 band; + /** Channel Switch time*/ + t_u16 switch_time; + /** Channel Switch timeout*/ + t_u16 switch_timeout; + /** Channel Regulatory class*/ + t_u8 regulatory_class; + /** Channel Switch periodicity*/ + t_u8 periodicity; +} __ATTRIB_PACK__ tdls_channel_switch; + +/** tdls stop channel switch */ +typedef struct _tdls_stop_chan_switch { + /** Action */ + t_u16 action; + /** Peer MAC address */ + t_u8 peer_mac[ETH_ALEN]; +} __ATTRIB_PACK__ tdls_stop_chan_switch; + +/** tdls disable channel switch */ +typedef struct _tdls_disable_cs { + /** Action */ + t_u16 action; + /** Data*/ + t_u16 data; +} __ATTRIB_PACK__ tdls_disable_cs, tdls_config; + +/** tdls channel switch parameters */ +typedef struct _tdls_cs_params { + /** Action */ + t_u16 action; + /** unit time, multiples of 10ms */ + t_u8 unit_time; + /** threshold for other link */ + t_u8 threshold_otherlink; + /** threshold for direct link */ + t_u8 threshold_directlink; +} __ATTRIB_PACK__ tdls_cs_params; + +/** tdls debug */ +typedef struct _tdls_debug { + /** Action */ + t_u16 action; + /** Data */ + t_u8 data[0]; +} __ATTRIB_PACK__ tdls_debug; + +/** TLV header */ +#define TLVHEADER /** Tag */ \ + t_u16 tag; \ + /** Length */ \ + t_u16 length + +/** Length of TLV header */ +#define TLVHEADER_LEN 4 + +/** Data structure for subband set */ +typedef struct _IEEEtypes_SubbandSet_t { + /** First channel */ + t_u8 first_chan; + /** Number of channels */ + t_u8 no_of_chan; + /** Maximum Tx power */ + t_u8 max_tx_pwr; +} __ATTRIB_PACK__ IEEEtypes_SubbandSet_t, *pIEEEtypes_SubbandSet_t; + +/** tlvbuf_DomainParamSet_t */ +typedef struct _tlvbuf_DomainParamSet { + /** Header */ + TLVHEADER; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[0]; +} __ATTRIB_PACK__ tlvbuf_DomainParamSet_t; + +/** Data structure of WMM QoS information */ +typedef struct _IEEEtypes_WmmQosInfo_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd:1; + /** Reserved */ + t_u8 reserved:3; + /** Parameter set count */ + t_u8 para_set_count:4; +#else + /** Parameter set count */ + t_u8 para_set_count:4; + /** Reserved */ + t_u8 reserved:3; + /** QoS UAPSD */ + t_u8 qos_uapsd:1; +#endif /* BIG_ENDIAN_SUPPORT */ +} __ATTRIB_PACK__ IEEEtypes_WmmQosInfo_t; + +/** Qos Info TLV */ +typedef struct _tlvbuf_QosInfo_t { + /** Header */ + TLVHEADER; + /** QosInfo */ + union { + /** QosInfo bitfield */ + IEEEtypes_WmmQosInfo_t qos_info; + /** QosInfo byte */ + t_u8 qos_info_byte; + } u; +} __ATTRIB_PACK__ tlvbuf_QosInfo_t; + +/** HT Capabilities Data */ +typedef struct _HTCap_t { + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; +} __ATTRIB_PACK__ HTCap_t, *pHTCap_t; + +/** HT Information Data */ +typedef struct _HTInfo_t { + /** Primary channel */ + t_u8 pri_chan; + /** Field 2 */ + t_u8 field2; + /** Field 3 */ + t_u16 field3; + /** Field 4 */ + t_u16 field4; + /** Bitmap indicating MCSs supported by all HT STAs in the BSS */ + t_u8 basic_mcs_set[16]; +} __ATTRIB_PACK__ HTInfo_t, *pHTInfo_t; + +/** 20/40 BSS Coexistence Data */ +typedef struct _BSSCo2040_t { + /** 20/40 BSS Coexistence value */ + t_u8 bss_co_2040_value; +} __ATTRIB_PACK__ BSSCo2040_t, *pBSSCo2040_t; + +/** HT Capabilities element */ +typedef struct _tlvbuf_HTCap_t { + /** Header */ + TLVHEADER; + /** HTCap struct */ + HTCap_t ht_cap; +} __ATTRIB_PACK__ tlvbuf_HTCap_t; + +/** HT Information element */ +typedef struct _tlvbuf_HTInfo_t { + /** Header */ + TLVHEADER; + + /** HTInfo struct */ + HTInfo_t ht_info; +} __ATTRIB_PACK__ tlvbuf_HTInfo_t; + +/** VHT MCS rate set field, refer to 802.11ac */ +typedef struct _VHT_MCS_set { + t_u16 rx_mcs_map; + t_u16 rx_max_rate; /* bit 29-31 reserved */ + t_u16 tx_mcs_map; + t_u16 tx_max_rate; /* bit 61-63 reserved */ +} __ATTRIB_PACK__ VHT_MCS_set_t, *pVHT_MCS_set_t; + +typedef struct _VHT_capa { + t_u32 vht_cap_info; + VHT_MCS_set_t mcs_sets; +} __ATTRIB_PACK__ VHT_capa_t, *pVHT_capa_t; + +/** VHT Capabilities IE */ +typedef struct _IEEEtypes_VHTCap_t { + /** Header */ + TLVHEADER; + /** VHTInfo struct */ + VHT_capa_t vht_cap; +} __ATTRIB_PACK__ tlvbuf_VHTCap_t, *ptlvbuf_VHTCap_t; + +/** VHT Operations IE */ +typedef struct _IEEEtypes_VHTOprat_t { + /** Header */ + TLVHEADER; + /** VHTOpra struct */ + t_u8 chan_width; + t_u8 chan_cf1; + t_u8 chan_cf2; + /** Basic MCS set map, each 2 bits stands for a Nss */ + t_u8 basic_mcs_map[VHT_MCS_MAP_LEN]; +} __ATTRIB_PACK__ tlvbuf_VHTOpra_t, *ptlvbuf_VHTOpra_t; + +/** 20/40 BSS Coexistence element */ +typedef struct _tlvbuf_2040BSSCo_t { + /** Header */ + TLVHEADER; + + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} __ATTRIB_PACK__ tlvbuf_2040BSSCo_t; + +/** Extended Capabilities element */ +typedef struct _tlvbuf_ExtCap_t { + /** Header */ + TLVHEADER; + /** ExtCap_t struct */ + t_u8 ext_cap[0]; +} __ATTRIB_PACK__ tlvbuf_ExtCap_t; + +/** tlvbuf_RatesParamSet_t */ +typedef struct _tlvbuf_RatesParamSet_t { + /** Header */ + TLVHEADER; + /** Rates */ + t_u8 rates[0]; +} __ATTRIB_PACK__ tlvbuf_RatesParamSet_t; + +/* IEEE Supported Channel sub-band description (7.3.2.19) */ +/* Sub-band description used in the supported channels element. */ +typedef struct _IEEEtypes_SupportChan_Subband_t { + t_u8 start_chan;/**< Starting channel in the subband */ + t_u8 num_chans; /**< Number of channels in the subband */ + +} __ATTRIB_PACK__ IEEEtypes_SupportChan_Subband_t; + +/* IEEE Supported Channel element (7.3.2.19) */ +/** + * Sent in association requests. Details the sub-bands and number + * of channels supported in each subband + */ +typedef struct _tlvbuf_SupportedChannels_t { + /** Header */ + TLVHEADER; + /**< IEEE Element ID = 36 */ + /** Configured sub-bands information in the element */ + IEEEtypes_SupportChan_Subband_t subband[0]; +} __ATTRIB_PACK__ tlvbuf_SupportedChannels_t; + +/* IEEE Supported Regulatory Classes description (7.3.2.54) */ +typedef struct _IEEEtypes_RegulatoryClass_t { + /** current regulatory class */ + t_u8 cur_regulatory_class; + /** supported regulatory class list */ + t_u8 regulatory_classes_list[0]; + +} __ATTRIB_PACK__ IEEEtypes_RegulatoryClass_t; + +/* IEEE Supported Regulatory Classes TLV (7.3.2.54) */ +typedef struct _tlvbuf_RegulatoryClass_t { + /** Header */ + TLVHEADER; + /**< IEEE Element ID = 59 */ + /** supported regulatory class */ + IEEEtypes_RegulatoryClass_t regulatory_class; +} __ATTRIB_PACK__ tlvbuf_RegulatoryClass_t; + +/** tlvbuf_RsnParamSet_t */ +typedef struct _tlvbuf_RsnParamSet_t { + /** Header */ + TLVHEADER; + /** Version */ + t_u16 version; + /** Group Cipher Suite */ + t_u8 group_cipher_suite[4]; + /** Pairwise Cipher Suite count */ + t_u16 pairwise_cipher_count; + /** Pairwise Cipher Suite */ + t_u8 pairwise_cipher_suite[0]; + /** AKM Suite Sount */ + t_u16 akm_suite_count; + /** AKM Suite */ + t_u8 akm_suite[0]; + /** RSN Capability */ + t_u16 rsn_capability; + /** PMKID Count */ + t_u16 pmkid_count; + /** PMKID list */ + t_u8 pmkid_list[0]; +} __ATTRIB_PACK__ tlvbuf_RsnParamSet_t; + +extern t_s32 sockfd; /**< socket */ +extern t_s8 dev_name[IFNAMSIZ + 1]; /**< device name */ + +#endif /* _MLANCONFIG_H_ */
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.c b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.c new file mode 100644 index 0000000..7ff73bc --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.c
@@ -0,0 +1,920 @@ +/** @file mlanhostcmd.c + * + * @brief This file contains mlanconfig helper functions + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 11/26/2008: initial version +************************************************************************/ + +#include "mlanconfig.h" +#include "mlanhostcmd.h" + +#ifndef MIN +/** Find minimum value */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief get hostcmd data + * + * @param ln A pointer to line number + * @param buf A pointer to hostcmd data + * @param size A pointer to the return size of hostcmd buffer + * @return MLAN_STATUS_SUCCESS + */ +static int +mlan_get_hostcmd_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size) +{ + t_s32 errors = 0, i; + t_s8 line[256], *pos, *pos1, *pos2, *pos3; + t_u16 len; + + while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) { + (*ln)++; + if (strcmp(pos, "}") == 0) { + break; + } + + pos1 = strchr(pos, ':'); + if (pos1 == NULL) { + printf("Line %d: Invalid hostcmd line '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos1++ = '\0'; + + pos2 = strchr(pos1, '='); + if (pos2 == NULL) { + printf("Line %d: Invalid hostcmd line '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos2++ = '\0'; + + len = a2hex_or_atoi(pos1); + if (len < 1 || len > MRVDRV_SIZE_OF_CMD_BUFFER) { + printf("Line %d: Invalid hostcmd line '%s'\n", *ln, + pos); + errors++; + continue; + } + + *size += len; + + if (*pos2 == '"') { + pos2++; + if ((pos3 = strchr(pos2, '"')) == NULL) { + printf("Line %d: invalid quotation '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos3 = '\0'; + memset(buf, 0, len); + memmove(buf, pos2, MIN(strlen(pos2), len)); + buf += len; + } else if (*pos2 == '\'') { + pos2++; + if ((pos3 = strchr(pos2, '\'')) == NULL) { + printf("Line %d: invalid quotation '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos3 = ','; + for (i = 0; i < len; i++) { + if ((pos3 = strchr(pos2, ',')) != NULL) { + *pos3 = '\0'; + *buf++ = (t_u8)a2hex_or_atoi(pos2); + pos2 = pos3 + 1; + } else + *buf++ = 0; + } + } else if (*pos2 == '{') { + t_u16 tlvlen = 0, tmp_tlvlen; + mlan_get_hostcmd_data(fp, ln, buf + len, &tlvlen); + tmp_tlvlen = tlvlen; + while (len--) { + *buf++ = (t_u8)(tmp_tlvlen & 0xff); + tmp_tlvlen >>= 8; + } + *size += tlvlen; + buf += tlvlen; + } else { + t_u32 value = a2hex_or_atoi(pos2); + while (len--) { + *buf++ = (t_u8)(value & 0xff); + value >>= 8; + } + } + } + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief convert char to hex integer + * + * @param chr char to convert + * @return hex integer or 0 + */ +int +hexval(t_s32 chr) +{ + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + return 0; +} + +/** + * @brief Hump hex data + * + * @param prompt A pointer prompt buffer + * @param p A pointer to data buffer + * @param len the len of data buffer + * @param delim delim char + * @return hex integer + */ +t_void +hexdump(t_s8 *prompt, t_void *p, t_s32 len, t_s8 delim) +{ + t_s32 i; + t_u8 *s = p; + + if (prompt) { + printf("%s: len=%d\n", prompt, (int)len); + } + for (i = 0; i < len; i++) { + if (i != len - 1) + printf("%02x%c", *s++, delim); + else + printf("%02x\n", *s); + if ((i + 1) % 16 == 0) + printf("\n"); + } + printf("\n"); +} + +/** + * @brief convert char to hex integer + * + * @param chr char + * @return hex integer + */ +t_u8 +hexc2bin(t_s8 chr) +{ + if (chr >= '0' && chr <= '9') + chr -= '0'; + else if (chr >= 'A' && chr <= 'F') + chr -= ('A' - 10); + else if (chr >= 'a' && chr <= 'f') + chr -= ('a' - 10); + + return chr; +} + +/** + * @brief convert string to hex integer + * + * @param s A pointer string buffer + * @return hex integer + */ +t_u32 +a2hex(t_s8 *s) +{ + t_u32 val = 0; + + if (!strncasecmp("0x", s, 2)) { + s += 2; + } + + while (*s && isxdigit(*s)) { + val = (val << 4) + hexc2bin(*s++); + } + + return val; +} + +/* + * @brief convert String to integer + * + * @param value A pointer to string + * @return integer + */ +t_u32 +a2hex_or_atoi(t_s8 *value) +{ + if (value[0] == '0' && (value[1] == 'X' || value[1] == 'x')) { + return a2hex(value + 2); + } else if (isdigit(*value)) { + return atoi(value); + } else { + return *value; + } +} + +/** + * @brief convert string to hex + * + * @param ptr A pointer to data buffer + * @param chr A pointer to return integer + * @return A pointer to next data field + */ +t_s8 * +convert2hex(t_s8 *ptr, t_u8 *chr) +{ + t_u8 val; + + for (val = 0; *ptr && isxdigit(*ptr); ptr++) { + val = (val * 16) + hexval(*ptr); + } + + *chr = val; + + return ptr; +} + +/** + * @brief Check the Hex String + * @param s A pointer to the string + * @return MLAN_STATUS_SUCCESS --HexString, MLAN_STATUS_FAILURE --not HexString + */ +int +ishexstring(t_s8 *s) +{ + int ret = MLAN_STATUS_FAILURE; + t_s32 tmp; + + if (!strncasecmp("0x", s, 2)) { + s += 2; + } + while (*s) { + tmp = toupper(*s); + if (((tmp >= 'A') && (tmp <= 'F')) || + ((tmp >= '0') && (tmp <= '9'))) { + ret = MLAN_STATUS_SUCCESS; + } else { + ret = MLAN_STATUS_FAILURE; + break; + } + s++; + } + + return ret; +} + +/** + * @brief Convert String to Integer + * @param buf A pointer to the string + * @return Integer + */ +int +atoval(t_s8 *buf) +{ + if (!strncasecmp(buf, "0x", 2)) + return a2hex(buf + 2); + else if (!ishexstring(buf)) + return a2hex(buf); + else + return atoi(buf); +} + +/** + * @brief Prepare host-command buffer + * @param fp File handler + * @param cmd_name Command name + * @param buf A pointer to comand buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf) +{ + t_s8 line[256], cmdname[256], *pos, cmdcode[10]; + HostCmd_DS_GEN *hostcmd; + int ln = 0; + int cmdname_found = 0, cmdcode_found = 0; + + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + hostcmd = (HostCmd_DS_GEN *)buf; + hostcmd->command = 0xffff; + + snprintf(cmdname, sizeof(cmdname), "%s={", cmd_name); + cmdname_found = 0; + while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) { + if (strcmp(pos, cmdname) == 0) { + cmdname_found = 1; + snprintf(cmdcode, sizeof(cmdcode), "CmdCode="); + cmdcode_found = 0; + while ((pos = + mlan_config_get_line(fp, line, sizeof(line), + &ln))) { + if (strncmp(pos, cmdcode, strlen(cmdcode)) == 0) { + cmdcode_found = 1; + hostcmd->command = + a2hex_or_atoi(pos + + strlen(cmdcode)); + hostcmd->size = S_DS_GEN; + mlan_get_hostcmd_data(fp, &ln, + buf + + hostcmd->size, + &hostcmd->size); + break; + } + } + if (!cmdcode_found) { + fprintf(stderr, + "mlanconfig: CmdCode not found in conf file\n"); + return MLAN_STATUS_FAILURE; + } + break; + } + } + + if (!cmdname_found) { + fprintf(stderr, + "mlanconfig: cmdname '%s' is not found in conf file\n", + cmd_name); + return MLAN_STATUS_FAILURE; + } + + hostcmd->seq_num = 0; + hostcmd->result = 0; + hostcmd->command = cpu_to_le16(hostcmd->command); + hostcmd->size = cpu_to_le16(hostcmd->size); + return MLAN_STATUS_SUCCESS; +} + +/** Config data header length */ +#define CFG_DATA_HEADER_LEN 6 + +/** + * @brief Prepare cfg-data buffer + * @param argc number of arguments + * @param argv A pointer to arguments array + * @param fp File handler + * @param buf A pointer to comand buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_cfg_data_buffer(int argc, char *argv[], FILE * fp, t_u8 *buf) +{ + int ln = 0, type; + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_802_11_CFG_DATA *pcfg_data; + + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + hostcmd = (HostCmd_DS_GEN *)buf; + hostcmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); + pcfg_data = (HostCmd_DS_802_11_CFG_DATA *)(buf + S_DS_GEN); + pcfg_data->action = + (argc == 4) ? HostCmd_ACT_GEN_GET : HostCmd_ACT_GEN_SET; + type = atoi(argv[3]); + if ((type < 1) || (type > 2)) { + fprintf(stderr, "mlanconfig: Invalid register type\n"); + return MLAN_STATUS_FAILURE; + } else { + pcfg_data->type = type; + } + if (argc == 5) { + ln = fparse_for_hex(fp, pcfg_data->data); + } + pcfg_data->data_len = ln; + hostcmd->size = + cpu_to_le16(pcfg_data->data_len + S_DS_GEN + + CFG_DATA_HEADER_LEN); + pcfg_data->data_len = cpu_to_le16(pcfg_data->data_len); + pcfg_data->type = cpu_to_le16(pcfg_data->type); + pcfg_data->action = cpu_to_le16(pcfg_data->action); + + hostcmd->seq_num = 0; + hostcmd->result = 0; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process host_cmd response + * @param buf A pointer to the response buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_host_cmd_resp(t_u8 *buf) +{ + HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf; + int ret = MLAN_STATUS_SUCCESS; + + hostcmd->command = le16_to_cpu(hostcmd->command); + hostcmd->size = le16_to_cpu(hostcmd->size); + hostcmd->seq_num = le16_to_cpu(hostcmd->seq_num); + hostcmd->result = le16_to_cpu(hostcmd->result); + + hostcmd->command &= ~HostCmd_RET_BIT; + if (!hostcmd->result) { + switch (hostcmd->command) { + case HostCmd_CMD_CFG_DATA: + { + HostCmd_DS_802_11_CFG_DATA *pstcfgData = + (HostCmd_DS_802_11_CFG_DATA *)(buf + + S_DS_GEN); + pstcfgData->data_len = + le16_to_cpu(pstcfgData->data_len); + pstcfgData->action = + le16_to_cpu(pstcfgData->action); + + if (pstcfgData->action == HostCmd_ACT_GEN_GET) { + hexdump("cfgdata", pstcfgData->data, + pstcfgData->data_len, ' '); + } + break; + } + case HostCmd_CMD_802_11_TPC_ADAPT_REQ: + { + mlan_ioctl_11h_tpc_resp *tpcIoctlResp = + (mlan_ioctl_11h_tpc_resp *)(buf + + S_DS_GEN); + if (tpcIoctlResp->status_code == 0) { + printf("tpcrequest: txPower(%d), linkMargin(%d), rssi(%d)\n", tpcIoctlResp->tx_power, tpcIoctlResp->link_margin, tpcIoctlResp->rssi); + } else { + printf("tpcrequest: failure, status = %d\n", tpcIoctlResp->status_code); + } + break; + } + case HostCmd_CMD_802_11_CRYPTO: + { + t_u16 alg = + le16_to_cpu((t_u16) + *(buf + S_DS_GEN + + sizeof(t_u16))); + if (alg != CIPHER_TEST_AES_CCM) { + HostCmd_DS_802_11_CRYPTO *cmd = + (HostCmd_DS_802_11_CRYPTO *)(buf + + + S_DS_GEN); + cmd->encdec = le16_to_cpu(cmd->encdec); + cmd->algorithm = + le16_to_cpu(cmd->algorithm); + cmd->key_IV_length = + le16_to_cpu(cmd->key_IV_length); + cmd->key_length = + le16_to_cpu(cmd->key_length); + cmd->data.header.type = + le16_to_cpu(cmd->data.header. + type); + cmd->data.header.len = + le16_to_cpu(cmd->data.header. + len); + + printf("crypto_result: encdec=%d algorithm=%d,KeyIVLen=%d," " KeyLen=%d,dataLen=%d\n", cmd->encdec, cmd->algorithm, cmd->key_IV_length, cmd->key_length, cmd->data.header.len); + hexdump("KeyIV", cmd->keyIV, + cmd->key_IV_length, ' '); + hexdump("Key", cmd->key, + cmd->key_length, ' '); + hexdump("Data", cmd->data.data, + cmd->data.header.len, ' '); + } else { + HostCmd_DS_802_11_CRYPTO_AES_CCM + *cmd_aes_ccm = + (HostCmd_DS_802_11_CRYPTO_AES_CCM + *)(buf + S_DS_GEN); + + cmd_aes_ccm->encdec + = + le16_to_cpu(cmd_aes_ccm-> + encdec); + cmd_aes_ccm->algorithm = + le16_to_cpu(cmd_aes_ccm-> + algorithm); + cmd_aes_ccm->key_length = + le16_to_cpu(cmd_aes_ccm-> + key_length); + cmd_aes_ccm->nonce_length = + le16_to_cpu(cmd_aes_ccm-> + nonce_length); + cmd_aes_ccm->AAD_length = + le16_to_cpu(cmd_aes_ccm-> + AAD_length); + cmd_aes_ccm->data.header.type = + le16_to_cpu(cmd_aes_ccm->data. + header.type); + cmd_aes_ccm->data.header.len = + le16_to_cpu(cmd_aes_ccm->data. + header.len); + + printf("crypto_result: encdec=%d algorithm=%d, KeyLen=%d," " NonceLen=%d,AADLen=%d,dataLen=%d\n", cmd_aes_ccm->encdec, cmd_aes_ccm->algorithm, cmd_aes_ccm->key_length, cmd_aes_ccm->nonce_length, cmd_aes_ccm->AAD_length, cmd_aes_ccm->data.header.len); + + hexdump("Key", cmd_aes_ccm->key, + cmd_aes_ccm->key_length, ' '); + hexdump("Nonce", cmd_aes_ccm->nonce, + cmd_aes_ccm->nonce_length, ' '); + hexdump("AAD", cmd_aes_ccm->AAD, + cmd_aes_ccm->AAD_length, ' '); + hexdump("Data", cmd_aes_ccm->data.data, + cmd_aes_ccm->data.header.len, + ' '); + } + break; + } + case HostCmd_CMD_802_11_AUTO_TX: + { + HostCmd_DS_802_11_AUTO_TX *at = + (HostCmd_DS_802_11_AUTO_TX *)(buf + + S_DS_GEN); + + if (le16_to_cpu(at->action) == + HostCmd_ACT_GEN_GET) { + if (S_DS_GEN + sizeof(at->action) == + hostcmd->size) { + printf("auto_tx not configured\n"); + + } else { + MrvlIEtypesHeader_t *header = + &at->auto_tx.header; + + header->type = + le16_to_cpu(header-> + type); + header->len = + le16_to_cpu(header-> + len); + + if ((S_DS_GEN + + sizeof(at->action) + + + sizeof(MrvlIEtypesHeader_t) + + header->len == + hostcmd->size) && + (header->type == + TLV_TYPE_AUTO_TX)) { + + AutoTx_MacFrame_t *atmf + = + &at->auto_tx. + auto_tx_mac_frame; + + printf("Interval: %d second(s)\n", le16_to_cpu(atmf->interval)); + printf("Priority: %#x\n", atmf->priority); + printf("Frame Length: %d\n", le16_to_cpu(atmf->frame_len)); + printf("Dest Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->dest_mac_addr[0], atmf->dest_mac_addr[1], atmf->dest_mac_addr[2], atmf->dest_mac_addr[3], atmf->dest_mac_addr[4], atmf->dest_mac_addr[5]); + printf("Src Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->src_mac_addr[0], atmf->src_mac_addr[1], atmf->src_mac_addr[2], atmf->src_mac_addr[3], atmf->src_mac_addr[4], atmf->src_mac_addr[5]); + + hexdump("Frame Payload", + atmf->payload, + le16_to_cpu + (atmf-> + frame_len) + - + MLAN_MAC_ADDR_LENGTH + * 2, ' '); + } else { + printf("incorrect auto_tx command response\n"); + } + } + } + break; + } + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + { + HostCmd_DS_802_11_SUBSCRIBE_EVENT *se = + (HostCmd_DS_802_11_SUBSCRIBE_EVENT + *)(buf + S_DS_GEN); + if (le16_to_cpu(se->action) == + HostCmd_ACT_GEN_GET) { + int len = + S_DS_GEN + + sizeof + (HostCmd_DS_802_11_SUBSCRIBE_EVENT); + printf("\nEvent\t\tValue\tFreq\tsubscribed\n\n"); + while (len < hostcmd->size) { + MrvlIEtypesHeader_t *header = + (MrvlIEtypesHeader_t + *)(buf + len); + switch (le16_to_cpu + (header->type)) { + case TLV_TYPE_RSSI_LOW: + { + MrvlIEtypes_RssiThreshold_t + *low_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Beacon Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0001) ? "yes" : "no"); + break; + } + case TLV_TYPE_SNR_LOW: + { + MrvlIEtypes_SnrThreshold_t + *low_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Beacon Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0002) ? "yes" : "no"); + break; + } + case TLV_TYPE_FAILCOUNT: + { + MrvlIEtypes_FailureCount_t + *failure_count + = + (MrvlIEtypes_FailureCount_t + *)(buf + + + len); + printf("Failure Count\t%d\t%d\t%s\n", failure_count->fail_value, failure_count->fail_freq, (le16_to_cpu(se->events) & 0x0004) ? "yes" : "no"); + break; + } + case TLV_TYPE_BCNMISS: + { + MrvlIEtypes_BeaconsMissed_t + *bcn_missed + = + (MrvlIEtypes_BeaconsMissed_t + *)(buf + + + len); + printf("Beacon Missed\t%d\tN/A\t%s\n", bcn_missed->beacon_missed, (le16_to_cpu(se->events) & 0x0008) ? "yes" : "no"); + break; + } + case TLV_TYPE_RSSI_HIGH: + { + MrvlIEtypes_RssiThreshold_t + *high_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Bcn High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0010) ? "yes" : "no"); + break; + } + + case TLV_TYPE_SNR_HIGH: + { + MrvlIEtypes_SnrThreshold_t + *high_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Beacon High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0020) ? "yes" : "no"); + break; + } + case TLV_TYPE_RSSI_LOW_DATA: + { + MrvlIEtypes_RssiThreshold_t + *low_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Data Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0040) ? "yes" : "no"); + break; + } + case TLV_TYPE_SNR_LOW_DATA: + { + MrvlIEtypes_SnrThreshold_t + *low_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Data Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0080) ? "yes" : "no"); + break; + } + case TLV_TYPE_RSSI_HIGH_DATA: + { + MrvlIEtypes_RssiThreshold_t + *high_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Data High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0100) ? "yes" : "no"); + break; + } + case TLV_TYPE_SNR_HIGH_DATA: + { + MrvlIEtypes_SnrThreshold_t + *high_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Data High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0200) ? "yes" : "no"); + break; + } + case TLV_TYPE_LINK_QUALITY: + { + MrvlIEtypes_LinkQuality_t + *link_qual + = + (MrvlIEtypes_LinkQuality_t + *)(buf + + + len); + printf("Link Quality Parameters:\n"); + printf("------------------------\n"); + printf("Link Quality Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0400) ? "yes" : "no"); + printf("Link SNR Threshold = %d\n", le16_to_cpu(link_qual->link_SNR_thrs)); + printf("Link SNR Frequency = %d\n", le16_to_cpu(link_qual->link_SNR_freq)); + printf("Min Rate Value = %d\n", le16_to_cpu(link_qual->min_rate_val)); + printf("Min Rate Frequency = %d\n", le16_to_cpu(link_qual->min_rate_freq)); + printf("Tx Latency Value = %d\n", le32_to_cpu(link_qual->tx_latency_val)); + printf("Tx Latency Threshold = %d\n", le32_to_cpu(link_qual->tx_latency_thrs)); + + break; + } + case TLV_TYPE_PRE_BEACON_LOST: + { + MrvlIEtypes_PreBeaconLost_t + *pre_bcn_lost + = + (MrvlIEtypes_PreBeaconLost_t + *)(buf + + + len); + printf("------------------------\n"); + printf("Pre-Beacon Lost Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0800) ? "yes" : "no"); + printf("Pre-Beacon Lost: %d\n", pre_bcn_lost->pre_beacon_lost); + break; + } + default: + printf("Unknown subscribed event TLV Type=%#x," " Len=%d\n", le16_to_cpu(header->type), le16_to_cpu(header->len)); + break; + } + + len += (sizeof + (MrvlIEtypesHeader_t) + + + le16_to_cpu(header-> + len)); + } + } + break; + } + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + { + HostCmd_DS_REG *preg = + (HostCmd_DS_REG *)(buf + S_DS_GEN); + preg->action = le16_to_cpu(preg->action); + if (preg->action == HostCmd_ACT_GEN_GET) { + preg->value = le32_to_cpu(preg->value); + printf("value = 0x%08x\n", preg->value); + } + break; + } + case HostCmd_CMD_MEM_ACCESS: + { + HostCmd_DS_MEM *pmem = + (HostCmd_DS_MEM *)(buf + S_DS_GEN); + pmem->action = le16_to_cpu(pmem->action); + if (pmem->action == HostCmd_ACT_GEN_GET) { + pmem->value = le32_to_cpu(pmem->value); + printf("value = 0x%08x\n", pmem->value); + } + break; + } + default: + printf("HOSTCMD_RESP: CmdCode=%#04x, Size=%#04x," + " SeqNum=%#04x, Result=%#04x\n", + hostcmd->command, hostcmd->size, + hostcmd->seq_num, hostcmd->result); + hexdump("payload", + (t_void *)(buf + S_DS_GEN), + hostcmd->size - S_DS_GEN, ' '); + break; + } + } else { + printf("HOSTCMD failed: CmdCode=%#04x, Size=%#04x," + " SeqNum=%#04x, Result=%#04x\n", + hostcmd->command, hostcmd->size, + hostcmd->seq_num, hostcmd->result); + } + return ret; +} + +/** + * @brief Prepare ARP filter buffer + * @param fp File handler + * @param buf A pointer to the buffer + * @param length A pointer to the length of buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_arp_filter_buffer(FILE * fp, t_u8 *buf, t_u16 *length) +{ + t_s8 line[256], *pos; + int ln = 0; + int ret = MLAN_STATUS_SUCCESS; + int arpfilter_found = 0; + + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) { + if (strcmp(pos, "arpfilter={") == 0) { + arpfilter_found = 1; + mlan_get_hostcmd_data(fp, &ln, buf, length); + break; + } + } + if (!arpfilter_found) { + fprintf(stderr, + "mlanconfig: 'arpfilter' not found in conf file"); + ret = MLAN_STATUS_FAILURE; + } + return ret; +} + +/** + * @brief Prepare the hostcmd for register access + * @param type Register type + * @param offset Register offset + * @param value Pointer to value (NULL for read) + * @param buf Pointer to hostcmd buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf) +{ + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_REG *preg; + + hostcmd = (HostCmd_DS_GEN *)buf; + switch (type) { + case 1: + hostcmd->command = cpu_to_le16(HostCmd_CMD_MAC_REG_ACCESS); + break; + case 2: + hostcmd->command = cpu_to_le16(HostCmd_CMD_BBP_REG_ACCESS); + break; + case 3: + hostcmd->command = cpu_to_le16(HostCmd_CMD_RF_REG_ACCESS); + break; + case 5: + hostcmd->command = cpu_to_le16(HostCmd_CMD_CAU_REG_ACCESS); + break; + default: + printf("Invalid register set specified\n"); + return -EINVAL; + } + preg = (HostCmd_DS_REG *)(buf + S_DS_GEN); + preg->action = (value) ? HostCmd_ACT_GEN_SET : HostCmd_ACT_GEN_GET; + preg->action = cpu_to_le16(preg->action); + preg->offset = cpu_to_le16((t_u16)offset); + if (value) + preg->value = cpu_to_le32(*value); + else + preg->value = 0; + hostcmd->size = cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_REG)); + hostcmd->seq_num = 0; + hostcmd->result = 0; + + return MLAN_STATUS_SUCCESS; +}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.h b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.h new file mode 100644 index 0000000..09d72ab --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/mlanhostcmd.h
@@ -0,0 +1,355 @@ +/** @file mlanhostcmd.h + * + * @brief This file contains command structures for mlanconfig application + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 11/26/2008: initial version +************************************************************************/ +#ifndef _MLANHOSTCMD_H_ +#define _MLANHOSTCMD_H_ + +/** Find number of elements */ +#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) +/** Size of command buffer */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Command RET code, MSB is set to 1 */ +#define HostCmd_RET_BIT 0x8000 +/** General purpose action : Get */ +#define HostCmd_ACT_GEN_GET 0x0000 +/** General purpose action : Set */ +#define HostCmd_ACT_GEN_SET 0x0001 +/** General purpose action : Clear */ +#define HostCmd_ACT_GEN_CLEAR 0x0004 +/** General purpose action : Remove */ +#define HostCmd_ACT_GEN_REMOVE 0x0004 + +/** Host Command ID : Memory access */ +#define HostCmd_CMD_MEM_ACCESS 0x0086 + +/** Pre-Authenticate - 11r only */ +#define HostCmd_CMD_802_11_AUTHENTICATE 0x0011 + +/** Read/Write Mac register */ +#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 +/** Read/Write BBP register */ +#define HostCmd_CMD_BBP_REG_ACCESS 0x001a +/** Read/Write RF register */ +#define HostCmd_CMD_RF_REG_ACCESS 0x001b +/** Get TX Power data */ +#define HostCmd_CMD_802_11_RF_TX_POWER 0x001e +/** Get the current TSF */ +#define HostCmd_CMD_GET_TSF 0x0080 +/** Host Command ID : CAU register access */ +#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed + +/** Host Command ID : 802.11 BG scan configuration */ +#define HostCmd_CMD_802_11_BG_SCAN_CONFIG 0x006b +/** Host Command ID : Configuration data */ +#define HostCmd_CMD_CFG_DATA 0x008f +/** Host Command ID : 802.11 TPC adapt req */ +#define HostCmd_CMD_802_11_TPC_ADAPT_REQ 0x0060 +/** Host Command ID : 802.11 crypto */ +#define HostCmd_CMD_802_11_CRYPTO 0x0078 +/** Host Command ID : 802.11 auto Tx */ +#define HostCmd_CMD_802_11_AUTO_TX 0x0082 + +/** Host Command ID : 802.11 subscribe event */ +#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 + +/** Host Command ID : Channel TRPC config */ +#define HostCmd_CMD_CHAN_TRPC_CONFIG 0x00fb + +/** TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 +/** TLV type : Beacon RSSI low */ +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 0x04) //0x0104 +/** TLV type : Beacon SNR low */ +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 0x05) //0x0105 +/** TLV type : Fail count */ +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 0x06) //0x0106 +/** TLV type : BCN miss */ +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 0x07) //0x0107 +/** TLV type : Beacon RSSI high */ +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 0x16) //0x0116 +/** TLV type : Beacon SNR high */ +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 0x17) //0x0117 +/** TLV type : Auto Tx */ +#define TLV_TYPE_AUTO_TX (PROPRIETARY_TLV_BASE_ID + 0x18) //0x0118 +/** TLV type :Link Quality */ +#define TLV_TYPE_LINK_QUALITY (PROPRIETARY_TLV_BASE_ID + 0x24) //0x0124 +/** TLV type : Data RSSI low */ +#define TLV_TYPE_RSSI_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x26) //0x0126 +/** TLV type : Data SNR low */ +#define TLV_TYPE_SNR_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x27) //0x0127 +/** TLV type : Data RSSI high */ +#define TLV_TYPE_RSSI_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x28) //0x0128 +/** TLV type : Data SNR high */ +#define TLV_TYPE_SNR_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x29) //0x0129 +/** TLV type: Pre-Beacon Lost */ +#define TLV_TYPE_PRE_BEACON_LOST (PROPRIETARY_TLV_BASE_ID + 0x49) //0x0149 + +/** TLV type : Channel TRPC */ +#define TLV_TYPE_CHAN_TRPC (PROPRIETARY_TLV_BASE_ID + 0x89) //0x0189 + +/* Define general hostcmd data structure */ +/** HostCmd_DS_GEN */ +typedef struct MAPP_HostCmd_DS_GEN { + /** Command */ + t_u16 command; + /** Size */ + t_u16 size; + /** Sequence number */ + t_u16 seq_num; + /** Result */ + t_u16 result; +} __ATTRIB_PACK__ HostCmd_DS_GEN; + +typedef struct _HostCmd_DS_MEF_CFG { + /** Criteria */ + t_u32 Criteria; + /** Number of entries */ + t_u16 NumEntries; +} __ATTRIB_PACK__ HostCmd_DS_MEF_CFG; + +typedef struct _MEF_CFG_DATA { + /** Size */ + t_u16 size; + /** Data */ + HostCmd_DS_MEF_CFG data; +} __ATTRIB_PACK__ MEF_CFG_DATA; + +/** Size of HostCmd_DS_GEN */ +#define S_DS_GEN sizeof(HostCmd_DS_GEN) + +/** HostCmd_DS_REG */ +typedef struct MAPP_HostCmd_DS_REG { + /** Read or write */ + t_u16 action; + /** Register offset */ + t_u16 offset; + /** Value */ + t_u32 value; +} __ATTRIB_PACK__ HostCmd_DS_REG; + +/** HostCmd_DS_MEM */ +typedef struct MAPP_HostCmd_DS_MEM { + /** Read or write */ + t_u16 action; + /** Reserved */ + t_u16 reserved; + /** Address */ + t_u32 addr; + /** Value */ + t_u32 value; +} __ATTRIB_PACK__ HostCmd_DS_MEM; + +/** HostCmd_DS_802_11_CFG_DATA */ +typedef struct MAPP_HostCmd_DS_802_11_CFG_DATA { + /** Action */ + t_u16 action; + /** Type */ + t_u16 type; + /** Data length */ + t_u16 data_len; + /** Data */ + t_u8 data[1]; +} __ATTRIB_PACK__ HostCmd_DS_802_11_CFG_DATA; + +/** mlan_ioctl_11h_tpc_resp */ +typedef struct { + int status_code; + /**< Firmware command result status code */ + int tx_power;/**< Reported TX Power from the TPC Report */ + int link_margin; + /**< Reported Link margin from the TPC Report */ + int rssi; /**< RSSI of the received TPC Report frame */ +} __ATTRIB_PACK__ mlan_ioctl_11h_tpc_resp; + +/** MrvlIEtypesHeader_t */ +typedef struct MrvlIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; +} __ATTRIB_PACK__ MrvlIEtypesHeader_t; + +/** MrvlIEtypes_Data_t */ +typedef struct MrvlIEtypes_Data_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Data */ + t_u8 data[1]; +} __ATTRIB_PACK__ MrvlIEtypes_Data_t; + +/** HostCmd_DS_802_11_CRYPTO */ +typedef struct MAPP_HostCmd_DS_802_11_CRYPTO { + t_u16 encdec; /**< Decrypt=0, Encrypt=1 */ + t_u16 algorithm; /**< RC4=1 AES=2 , AES_KEY_WRAP=3 */ + t_u16 key_IV_length;/**< Length of Key IV (bytes) */ + t_u8 keyIV[32]; /**< Key IV */ + t_u16 key_length; /**< Length of Key (bytes) */ + t_u8 key[32]; /**< Key */ + MrvlIEtypes_Data_t data; /**< Plain text if encdec=Encrypt, Ciphertext data if encdec=Decrypt*/ +} __ATTRIB_PACK__ HostCmd_DS_802_11_CRYPTO; + +/** HostCmd_DS_802_11_CRYPTO_AES_CCM */ +typedef struct MAPP_HostCmd_DS_802_11_CRYPTO_AES_CCM { + t_u16 encdec; /**< Decrypt=0, Encrypt=1 */ + t_u16 algorithm; /**< AES_CCM=4 */ + t_u16 key_length; /**< Length of Key (bytes) */ + t_u8 key[32]; /**< Key */ + t_u16 nonce_length;/**< Length of Nonce (bytes) */ + t_u8 nonce[14]; /**< Nonce */ + t_u16 AAD_length; /**< Length of AAD (bytes) */ + t_u8 AAD[32]; /**< AAD */ + MrvlIEtypes_Data_t data; /**< Plain text if encdec=Encrypt, Ciphertext data if encdec=Decrypt*/ +} __ATTRIB_PACK__ HostCmd_DS_802_11_CRYPTO_AES_CCM; + +/** AES CCM cipher test */ +#define CIPHER_TEST_AES_CCM (4) +/** AutoTx_MacFrame_t */ +typedef struct AutoTx_MacFrame { + t_u16 interval; /**< in seconds */ + t_u8 priority; /**< User Priority: 0~7, ignored if non-WMM */ + t_u8 reserved; /**< set to 0 */ + t_u16 frame_len; /**< Length of MAC frame payload */ + t_u8 dest_mac_addr[MLAN_MAC_ADDR_LENGTH]; /**< Destination MAC address */ + t_u8 src_mac_addr[MLAN_MAC_ADDR_LENGTH]; /**< Source MAC address */ + t_u8 payload[]; /**< Payload */ +} __ATTRIB_PACK__ AutoTx_MacFrame_t; + +/** MrvlIEtypes_AutoTx_t */ +typedef struct MrvlIEtypes_AutoTx { + MrvlIEtypesHeader_t header; /**< Header */ + AutoTx_MacFrame_t auto_tx_mac_frame; /**< Auto Tx MAC frame */ +} __ATTRIB_PACK__ MrvlIEtypes_AutoTx_t; + +/** HostCmd_DS_802_11_AUTO_TX */ +typedef struct MAPP_HostCmd_DS_802_11_AUTO_TX { + /** Action */ + t_u16 action; /* 0 = ACT_GET; 1 = ACT_SET; */ + MrvlIEtypes_AutoTx_t auto_tx; /**< Auto Tx */ +} __ATTRIB_PACK__ HostCmd_DS_802_11_AUTO_TX; + +/** HostCmd_DS_802_11_SUBSCRIBE_EVENT */ +typedef struct MAPP_HostCmd_DS_802_11_SUBSCRIBE_EVENT { + /** Action */ + t_u16 action; + /** Events */ + t_u16 events; +} __ATTRIB_PACK__ HostCmd_DS_802_11_SUBSCRIBE_EVENT; + +/** MrvlIEtypes_RssiParamSet_t */ +typedef struct MrvlIEtypes_RssiThreshold { + /** Header */ + MrvlIEtypesHeader_t header; + /** RSSI value */ + t_u8 RSSI_value; + /** RSSI frequency */ + t_u8 RSSI_freq; +} __ATTRIB_PACK__ MrvlIEtypes_RssiThreshold_t; + +/** MrvlIEtypes_SnrThreshold_t */ +typedef struct MrvlIEtypes_SnrThreshold { + /** Header */ + MrvlIEtypesHeader_t header; + /** SNR value */ + t_u8 SNR_value; + /** SNR frequency */ + t_u8 SNR_freq; +} __ATTRIB_PACK__ MrvlIEtypes_SnrThreshold_t; + +/** MrvlIEtypes_FailureCount_t */ +typedef struct MrvlIEtypes_FailureCount { + /** Header */ + MrvlIEtypesHeader_t header; + /** Failure value */ + t_u8 fail_value; + /** Failure frequency */ + t_u8 fail_freq; +} __ATTRIB_PACK__ MrvlIEtypes_FailureCount_t; + +/** MrvlIEtypes_BeaconsMissed_t */ +typedef struct MrvlIEtypes_BeaconsMissed { + /** Header */ + MrvlIEtypesHeader_t header; + /** Number of beacons missed */ + t_u8 beacon_missed; + /** Reserved */ + t_u8 reserved; +} __ATTRIB_PACK__ MrvlIEtypes_BeaconsMissed_t; + +/** MrvlIEtypes_LinkQuality_t */ +typedef struct MrvlIEtypes_LinkQuality { + /** Header */ + MrvlIEtypesHeader_t header; + /** Link SNR threshold */ + t_u16 link_SNR_thrs; + /** Link SNR frequency */ + t_u16 link_SNR_freq; + /** Minimum rate value */ + t_u16 min_rate_val; + /** Minimum rate frequency */ + t_u16 min_rate_freq; + /** Tx latency value */ + t_u32 tx_latency_val; + /** Tx latency threshold */ + t_u32 tx_latency_thrs; +} __ATTRIB_PACK__ MrvlIEtypes_LinkQuality_t; + +/** MrvlIEtypes_PreBeaconLost_t */ +typedef struct MrvlIEtypes_PreBeaconLost { + /** Header */ + MrvlIEtypesHeader_t header; + /** Pre-Beacon Lost */ + t_u8 pre_beacon_lost; + /** Reserved */ + t_u8 reserved; +} __ATTRIB_PACK__ MrvlIEtypes_PreBeaconLost_t; + +/* String helper functions */ +/** Convert char to hex integer */ +int hexval(t_s32 chr); +/** Convert char to hex integer */ +t_u8 hexc2bin(t_s8 chr); +/** Convert string to hex integer */ +t_u32 a2hex(t_s8 *s); +/** Check the Hex String */ +int ishexstring(t_s8 *s); +/** Convert String to integer */ +t_u32 a2hex_or_atoi(t_s8 *value); +/** Convert String to Integer */ +int atoval(t_s8 *buf); +/** Hump hex data */ +void hexdump(t_s8 *prompt, void *p, t_s32 len, t_s8 delim); +/** Convert String to Hex */ +t_s8 *convert2hex(t_s8 *ptr, t_u8 *chr); + +int process_host_cmd_resp(t_u8 *buf); +int prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf); +int prepare_arp_filter_buffer(FILE * fp, t_u8 *buf, t_u16 *length); +int prepare_cfg_data_buffer(int argc, char *argv[], FILE * fp, t_u8 *buf); +int prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf); + +#endif /* _MLANHOSTCMD_H_ */
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanmisc.c b/wlan_sd8897/mapp/mlanconfig/mlanmisc.c new file mode 100644 index 0000000..8d3399f --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/mlanmisc.c
@@ -0,0 +1,1161 @@ +/** @file mlanmisc.c + * + * @brief Program to prepare command buffer + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 03/10/2009: initial version +************************************************************************/ + +#include "mlanconfig.h" +#include "mlanhostcmd.h" +#include "mlanmisc.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Helper function for process_getscantable_idx + * + * @param pbuf A pointer to the buffer + * @param buf_len buffer length + * + * @return NA + * + */ +static void +dump_scan_elems(const t_u8 *pbuf, uint buf_len) +{ + uint idx; + uint marker = 2 + pbuf[1]; + + for (idx = 0; idx < buf_len; idx++) { + if (idx % 0x10 == 0) { + printf("\n%04x: ", idx); + } + + if (idx == marker) { + printf("|"); + marker = idx + pbuf[idx + 1] + 2; + } else { + printf(" "); + } + + printf("%02x ", pbuf[idx]); + } + + printf("\n"); +} + +/** + * @brief Helper function for process_getscantable_idx + * Find next element + * + * @param pp_ie_out pointer of a IEEEtypes_Generic_t structure pointer + * @param p_buf_left integer pointer, which contains the number of left p_buf + * + * @return MLAN_STATUS_SUCCESS on success, otherwise MLAN_STATUS_FAILURE + */ +static int +scantable_elem_next(IEEEtypes_Generic_t **pp_ie_out, int *p_buf_left) +{ + IEEEtypes_Generic_t *pie_gen; + t_u8 *p_next; + + if (*p_buf_left < 2) { + return MLAN_STATUS_FAILURE; + } + + pie_gen = *pp_ie_out; + + p_next = (t_u8 *)pie_gen + (pie_gen->ieee_hdr.len + + sizeof(pie_gen->ieee_hdr)); + *p_buf_left -= (p_next - (t_u8 *)pie_gen); + + *pp_ie_out = (IEEEtypes_Generic_t *)p_next; + + if (*p_buf_left <= 0) { + return MLAN_STATUS_FAILURE; + } + + return MLAN_STATUS_SUCCESS; +} + + /** + * @brief Helper function for process_getscantable_idx + * scantable find element + * + * @param ie_buf pointer of the IE buffer + * @param ie_buf_len IE buffer length + * @param ie_type IE type + * @param ppie_out pointer to the IEEEtypes_Generic_t structure pointer + * @return MLAN_STATUS_SUCCESS on success, otherwise MLAN_STATUS_FAILURE + */ +static int +scantable_find_elem(t_u8 *ie_buf, + unsigned int ie_buf_len, + IEEEtypes_ElementId_e ie_type, + IEEEtypes_Generic_t **ppie_out) +{ + int found; + unsigned int ie_buf_left; + + ie_buf_left = ie_buf_len; + + found = FALSE; + + *ppie_out = (IEEEtypes_Generic_t *)ie_buf; + + do { + found = ((*ppie_out)->ieee_hdr.element_id == ie_type); + + } while (!found && + (scantable_elem_next(ppie_out, (int *)&ie_buf_left) == 0)); + + if (!found) { + *ppie_out = NULL; + } + + return (found ? MLAN_STATUS_SUCCESS : MLAN_STATUS_FAILURE); +} + + /** + * @brief Helper function for process_getscantable_idx + * It gets SSID from IE + * + * @param ie_buf IE buffer + * @param ie_buf_len IE buffer length + * @param pssid SSID + * @param ssid_buf_max size of SSID + * @return MLAN_STATUS_SUCCESS on success, otherwise MLAN_STATUS_FAILURE + */ +static int +scantable_get_ssid_from_ie(t_u8 *ie_buf, + unsigned int ie_buf_len, + t_u8 *pssid, unsigned int ssid_buf_max) +{ + int retval; + IEEEtypes_Generic_t *pie_gen; + + retval = scantable_find_elem(ie_buf, ie_buf_len, SSID, &pie_gen); + + memcpy(pssid, pie_gen->data, MIN(pie_gen->ieee_hdr.len, ssid_buf_max)); + + return retval; +} + +/** + * @brief Display detailed information for a specific scan table entry + * + * @param prsp_info_req Scan table entry request structure + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_getscantable_idx(wlan_ioctl_get_scan_table_info *prsp_info_req) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + t_u8 *pcurrent; + int ret = 0; + char ssid[33]; + t_u16 tmp_cap; + t_u8 tsf[8]; + t_u16 beacon_interval; + t_u8 *scan_rsp_buf = NULL; + t_u16 cap_info; + wlan_ioctl_get_scan_table_info *prsp_info; + + wlan_get_scan_table_fixed fixed_fields; + t_u32 fixed_field_length; + t_u32 bss_info_length; + + scan_rsp_buf = (t_u8 *)malloc(SCAN_RESP_BUF_SIZE); + if (scan_rsp_buf == NULL) { + printf("Error: allocate memory for scan_rsp_buf failed\n"); + return -ENOMEM; + } + memset(ssid, 0x00, sizeof(ssid)); + + prsp_info = (wlan_ioctl_get_scan_table_info *)scan_rsp_buf; + + memcpy(prsp_info, prsp_info_req, + sizeof(wlan_ioctl_get_scan_table_info)); + + if (get_priv_ioctl("getscantable", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + ret = -EOPNOTSUPP; + goto done; + } + + /* + * Set up and execute the ioctl call + */ + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = (caddr_t) prsp_info; + iwr.u.data.length = SCAN_RESP_BUF_SIZE; + iwr.u.data.flags = subioctl_val; + + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("mlanconfig: getscantable ioctl"); + ret = -EFAULT; + goto done; + } + + if (prsp_info->scan_number == 0) { + printf("mlanconfig: getscantable ioctl - index out of range\n"); + ret = -EINVAL; + goto done; + } + + pcurrent = prsp_info->scan_table_entry_buf; + + memcpy((t_u8 *)&fixed_field_length, + (t_u8 *)pcurrent, sizeof(fixed_field_length)); + pcurrent += sizeof(fixed_field_length); + + memcpy((t_u8 *)&bss_info_length, + (t_u8 *)pcurrent, sizeof(bss_info_length)); + pcurrent += sizeof(bss_info_length); + + memcpy((t_u8 *)&fixed_fields, (t_u8 *)pcurrent, sizeof(fixed_fields)); + pcurrent += fixed_field_length; + + /* time stamp is 8 byte long */ + memcpy(tsf, pcurrent, sizeof(tsf)); + pcurrent += sizeof(tsf); + bss_info_length -= sizeof(tsf); + + /* beacon interval is 2 byte long */ + memcpy(&beacon_interval, pcurrent, sizeof(beacon_interval)); + pcurrent += sizeof(beacon_interval); + bss_info_length -= sizeof(beacon_interval); + + /* capability information is 2 byte long */ + memcpy(&cap_info, pcurrent, sizeof(cap_info)); + pcurrent += sizeof(cap_info); + bss_info_length -= sizeof(cap_info); + + scantable_get_ssid_from_ie(pcurrent, + bss_info_length, (t_u8 *)ssid, sizeof(ssid)); + + printf("\n*** [%s], %02x:%02x:%02x:%02x:%02x:%2x\n", + ssid, + fixed_fields.bssid[0], + fixed_fields.bssid[1], + fixed_fields.bssid[2], + fixed_fields.bssid[3], + fixed_fields.bssid[4], fixed_fields.bssid[5]); + memcpy(&tmp_cap, &cap_info, sizeof(tmp_cap)); + printf("Channel = %d, SS = %d, CapInfo = 0x%04x, BcnIntvl = %d\n", + fixed_fields.channel, + 255 - fixed_fields.rssi, tmp_cap, beacon_interval); + + printf("TSF Values: AP(0x%02x%02x%02x%02x%02x%02x%02x%02x), ", + tsf[7], tsf[6], tsf[5], tsf[4], tsf[3], tsf[2], tsf[1], tsf[0]); + + printf("Network(0x%016llx)\n", fixed_fields.network_tsf); + printf("\n"); + printf("Element Data (%d bytes)\n", (int)bss_info_length); + printf("------------"); + dump_scan_elems(pcurrent, bss_info_length); + printf("\n"); + +done: + + if (scan_rsp_buf) + free(scan_rsp_buf); + + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** Maximum SDIO command-52 buffer length */ +#define CMD52_BUF_LEN 3 + +/** + * @brief SD comand52 read/write + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_sdcmd52rw(int argc, char *argv[]) +{ + struct iwreq iwr; + int ioctl_val, subioctl_val, buf[CMD52_BUF_LEN]; + + if (get_priv_ioctl("sdcmd52rw", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + if (argc == 5) { + /* CMD52 read */ + iwr.u.data.length = CMD52_BUF_LEN - 1; + } else if (argc == 6) { + /* CMD52 write */ + buf[2] = atoval(argv[5]); /* data */ + iwr.u.data.length = CMD52_BUF_LEN; + } else { + fprintf(stderr, "Invalid number of parameters!\n"); + return MLAN_STATUS_FAILURE; + } + buf[0] = atoval(argv[3]); /* func */ + buf[1] = atoval(argv[4]); /* reg */ + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.flags = subioctl_val; + iwr.u.data.pointer = (caddr_t) buf; + + if (ioctl(sockfd, ioctl_val, &iwr)) { + perror("mlanconfig"); + fprintf(stderr, + "mlanconfig: CMD52 R/W not supported by " + "interface %s\n", dev_name); + return MLAN_STATUS_FAILURE; + } + printf("sdcmd52rw returns 0x%02X\n", buf[0]); + + return MLAN_STATUS_SUCCESS; +} + +/** Maximum SDIO command-53 buffer length */ +#define CMD53_BUF_LEN 2000 + +/** + * @brief SD comand53 read/write + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_sdcmd53rw(int argc, char *argv[]) +{ + struct iwreq iwr; + t_s8 *buf = NULL; + int addr, mode, blklen, blknum, i, rw; + int ioctl_val, subioctl_val; + + if (get_priv_ioctl("sdcmd53rw", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + if (argc < 8) { + fprintf(stderr, "Invalid number of parameters!\n"); + return MLAN_STATUS_FAILURE; + } + + if (!(buf = malloc(CMD53_BUF_LEN))) + return -ENOMEM; + memset(buf, 0, CMD53_BUF_LEN); + + if (argc == 8) { + rw = buf[0] = 0; /* CMD53 read */ + } else { + rw = buf[0] = 1; /* CMD53 write */ + } + buf[1] = atoval(argv[3]); /* func */ + addr = atoval(argv[4]); /* address */ + buf[2] = addr & 0xff; + buf[3] = (addr >> 8) & 0xff; + buf[4] = (addr >> 16) & 0xff; + buf[5] = (addr >> 24) & 0xff; + mode = atoval(argv[5]); /* mode */ + buf[6] = (t_u8)mode; + blklen = atoval(argv[6]); /* block size */ + buf[7] = blklen & 0xff; + buf[8] = (blklen >> 8) & 0xff; + blknum = atoval(argv[7]); /* block number or byte number */ + buf[9] = blknum & 0xff; + buf[10] = (blknum >> 8) & 0xff; + iwr.u.data.length = 11; + if (buf[0]) { + for (i = 0; i < (argc - 8); i++) + buf[11 + i] = atoval(argv[8 + i]); + iwr.u.data.length += (argc - 8); + } + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.flags = subioctl_val; + iwr.u.data.pointer = (caddr_t) buf; + + if (ioctl(sockfd, ioctl_val, &iwr)) { + perror("mlanconfig"); + fprintf(stderr, + "mlanconfig: CMD53 R/W not supported by " + "interface %s\n", dev_name); + free(buf); + return MLAN_STATUS_FAILURE; + } + + if (mode) { + fprintf(stderr, "CMD53rw blklen = %d, blknum = %d\n", blklen, + blknum); + } else { + blklen = 1; + fprintf(stderr, "CMD53rw bytelen = %d\n", blknum); + } + if (!rw) + hexdump("data", buf, blklen * blknum, ' '); + + free(buf); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Retrieve and display the contents of the driver scan table. + * + * The ioctl to retrieve the scan table contents will be invoked, and portions + * of the scan data will be displayed on stdout. The entire beacon or + * probe response is also retrieved (if available in the driver). This + * data would be needed in case the application was explicitly controlling + * the association (inserting IEs, TLVs, etc). + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_getscantable(int argc, char *argv[]) +{ + int ioctl_val, subioctl_val; + struct iwreq iwr; + t_u8 *scan_rsp_buf = NULL; + + struct wlan_ioctl_get_scan_list *scan_list_head = NULL; + struct wlan_ioctl_get_scan_list *scan_list_node = NULL; + struct wlan_ioctl_get_scan_list *curr = NULL, *prev = NULL, *next = + NULL; + + t_u32 total_scan_res = 0; + + unsigned int scan_start; + int idx, ret = 0; + + t_u8 *pcurrent; + t_u8 *pnext; + IEEEtypes_ElementId_e *pelement_id; + t_u8 *pelement_len; + int ssid_idx; + t_u8 *pbyte; + t_u16 new_ss; + t_u16 curr_ss; + + IEEEtypes_VendorSpecific_t *pwpa_ie; + const t_u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; + + IEEEtypes_WmmParameter_t *pwmm_ie; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + IEEEtypes_VendorSpecific_t *pwps_ie; + const t_u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + + int displayed_info; + + wlan_ioctl_get_scan_table_info rspInfoReq; + wlan_ioctl_get_scan_table_info *prsp_info; + + wlan_get_scan_table_fixed fixed_fields; + t_u32 fixed_field_length; + t_u32 bss_info_length; + wlan_ioctl_get_bss_info *bss_info; + + scan_rsp_buf = (t_u8 *)malloc(SCAN_RESP_BUF_SIZE); + if (scan_rsp_buf == NULL) { + printf("Error: allocate memory for scan_rsp_buf failed\n"); + return -ENOMEM; + } + prsp_info = (wlan_ioctl_get_scan_table_info *)scan_rsp_buf; + + if (get_priv_ioctl("getscantable", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + ret = -EOPNOTSUPP; + goto done; + } + + if (argc > 3 && (strcmp(argv[3], "tsf") != 0) + && (strcmp(argv[3], "help") != 0)) { + + idx = strtol(argv[3], NULL, 10); + + if (idx >= 0) { + if (scan_rsp_buf) { + free(scan_rsp_buf); + } + rspInfoReq.scan_number = idx; + return process_getscantable_idx(&rspInfoReq); + } + } + + displayed_info = FALSE; + scan_start = 1; + + do { + prsp_info->scan_number = scan_start; + + /* + * Set up and execute the ioctl call + */ + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = (caddr_t) prsp_info; + iwr.u.data.length = SCAN_RESP_BUF_SIZE; + iwr.u.data.flags = subioctl_val; + + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("mlanconfig: getscantable ioctl"); + ret = -EFAULT; + goto done; + } + total_scan_res += prsp_info->scan_number; + + pcurrent = 0; + pnext = prsp_info->scan_table_entry_buf; + + for (idx = 0; (unsigned int)idx < prsp_info->scan_number; idx++) { + + /* Alloc memory for new node for next BSS */ + scan_list_node = (struct wlan_ioctl_get_scan_list *) + malloc(sizeof(struct wlan_ioctl_get_scan_list)); + if (scan_list_node == NULL) { + printf("Error: allocate memory for scan_list_head failed\n"); + return -ENOMEM; + } + memset(scan_list_node, 0, + sizeof(struct wlan_ioctl_get_scan_list)); + + /* + * Set pcurrent to pnext in case pad bytes are at the end + * of the last IE we processed. + */ + pcurrent = pnext; + + /* Start extracting each BSS to prepare a linked list */ + memcpy((t_u8 *)&fixed_field_length, + (t_u8 *)pcurrent, sizeof(fixed_field_length)); + pcurrent += sizeof(fixed_field_length); + + memcpy((t_u8 *)&bss_info_length, + (t_u8 *)pcurrent, sizeof(bss_info_length)); + pcurrent += sizeof(bss_info_length); + + memcpy((t_u8 *)&fixed_fields, + (t_u8 *)pcurrent, sizeof(fixed_fields)); + pcurrent += fixed_field_length; + + scan_list_node->fixed_buf.fixed_field_length = + fixed_field_length; + scan_list_node->fixed_buf.bss_info_length = + bss_info_length; + scan_list_node->fixed_buf.fixed_fields = fixed_fields; + + bss_info = &scan_list_node->bss_info_buf; + + /* Set next to be the start of the next scan entry */ + pnext = pcurrent + bss_info_length; + + if (bss_info_length >= + (sizeof(bss_info->tsf) + + sizeof(bss_info->beacon_interval) + + sizeof(bss_info->cap_info))) { + + /* time stamp is 8 byte long */ + memcpy(bss_info->tsf, pcurrent, + sizeof(bss_info->tsf)); + pcurrent += sizeof(bss_info->tsf); + bss_info_length -= sizeof(bss_info->tsf); + + /* beacon interval is 2 byte long */ + memcpy(&bss_info->beacon_interval, pcurrent, + sizeof(bss_info->beacon_interval)); + pcurrent += sizeof(bss_info->beacon_interval); + bss_info_length -= + sizeof(bss_info->beacon_interval); + + /* capability information is 2 byte long */ + memcpy(&bss_info->cap_info, pcurrent, + sizeof(bss_info->cap_info)); + pcurrent += sizeof(bss_info->cap_info); + bss_info_length -= sizeof(bss_info->cap_info); + } + + bss_info->wmm_cap = ' '; /* M (WMM), C (WMM-Call Admission Control) */ + bss_info->wps_cap = ' '; /* "S" */ + bss_info->dot11k_cap = ' '; /* "K" */ + bss_info->dot11r_cap = ' '; /* "R" */ + bss_info->ht_cap = ' '; /* "N" */ + + /* "P" for Privacy (WEP) since "W" is WPA, and "2" is RSN/WPA2 */ + bss_info->priv_cap = + bss_info->cap_info.privacy ? 'P' : ' '; + + memset(bss_info->ssid, 0, MRVDRV_MAX_SSID_LENGTH + 1); + bss_info->ssid_len = 0; + + while (bss_info_length >= 2) { + pelement_id = (IEEEtypes_ElementId_e *)pcurrent; + pelement_len = pcurrent + 1; + pcurrent += 2; + + switch (*pelement_id) { + + case SSID: + if (*pelement_len && + *pelement_len <= + MRVDRV_MAX_SSID_LENGTH) { + memcpy(bss_info->ssid, pcurrent, + *pelement_len); + bss_info->ssid_len = + *pelement_len; + } + break; + + case WPA_IE: + pwpa_ie = + (IEEEtypes_VendorSpecific_t *) + pelement_id; + if ((memcmp + (pwpa_ie->vend_hdr.oui, wpa_oui, + sizeof(pwpa_ie->vend_hdr.oui)) == + 0) + && (pwpa_ie->vend_hdr.oui_type == + wpa_oui[3])) { + /* WPA IE found, 'W' for WPA */ + bss_info->priv_cap = 'W'; + } else { + pwmm_ie = + (IEEEtypes_WmmParameter_t + *)pelement_id; + if ((memcmp + (pwmm_ie->vend_hdr.oui, + wmm_oui, + sizeof(pwmm_ie->vend_hdr. + oui)) == 0) + && (pwmm_ie->vend_hdr. + oui_type == + wmm_oui[3])) { + /* Check the subtype: 1 == parameter, 0 == info */ + if ((pwmm_ie->vend_hdr. + oui_subtype == 1) + && pwmm_ie-> + ac_params + [WMM_AC_VO]. + aci_aifsn.acm) { + /* Call admission on VO; 'C' for CAC */ + bss_info-> + wmm_cap + = 'C'; + } else { + /* No CAC; 'M' for uh, WMM */ + bss_info-> + wmm_cap + = 'M'; + } + } else { + pwps_ie = + (IEEEtypes_VendorSpecific_t + *)pelement_id; + if ((memcmp + (pwps_ie->vend_hdr. + oui, wps_oui, + sizeof(pwps_ie-> + vend_hdr. + oui)) == 0) + && (pwps_ie-> + vend_hdr. + oui_type == + wps_oui[3])) { + bss_info-> + wps_cap + = 'S'; + } + } + } + break; + + case RSN_IE: + /* RSN IE found; '2' for WPA2 (RSN) */ + bss_info->priv_cap = '2'; + break; + case HT_CAPABILITY: + bss_info->ht_cap = 'N'; + break; + case VHT_CAPABILITY: + bss_info->vht_cap[0] = 'A'; + bss_info->vht_cap[1] = 'C'; + break; + default: + break; + } + + pcurrent += *pelement_len; + bss_info_length -= (2 + *pelement_len); + } + + /* Create a sorted list of BSS using Insertion Sort. + Sort as per Signal Strength (descending order) */ + new_ss = 255 - fixed_fields.rssi; + + if (scan_list_head == NULL) { + // Node is the first element in the list. + scan_list_head = scan_list_node; + scan_list_node->next = NULL; + } else { + + curr_ss = + 255 - + scan_list_head->fixed_buf.fixed_fields. + rssi; + + if (new_ss > curr_ss) { + // Insert the node to head of the list + scan_list_node->next = scan_list_head; + scan_list_head = scan_list_node; + } else { + + for (curr = scan_list_head; + curr != NULL; curr = curr->next) { + + curr_ss = + 255 - + curr->fixed_buf. + fixed_fields.rssi; + if (new_ss > curr_ss) { + // Insert the node to current position in list + scan_list_node->next = + curr; + prev->next = + scan_list_node; + break; + } + prev = curr; + } + if (curr == NULL) { + + // Insert the node to tail of the list + prev->next = scan_list_node; + scan_list_node->next = NULL; + } + } + } + } + + scan_start += prsp_info->scan_number; + } while (prsp_info->scan_number); + + // Display scan results + printf("---------------------------------------"); + printf("---------------------------------------\n"); + printf("# | ch | ss | bssid | cap | SSID \n"); + printf("---------------------------------------"); + printf("---------------------------------------\n"); + + for (curr = scan_list_head, idx = 0; + (curr != NULL) && ((unsigned int)idx < total_scan_res); + curr = curr->next, idx++) { + + fixed_fields = curr->fixed_buf.fixed_fields; + bss_info = &curr->bss_info_buf; + + printf("%02u| %03d | %03d | %02x:%02x:%02x:%02x:%02x:%02x |", + idx, + fixed_fields.channel, + 255 - fixed_fields.rssi, + fixed_fields.bssid[0], + fixed_fields.bssid[1], + fixed_fields.bssid[2], + fixed_fields.bssid[3], + fixed_fields.bssid[4], fixed_fields.bssid[5]); + + displayed_info = TRUE; + + /* "A" for Adhoc + * "I" for Infrastructure, + * "D" for DFS (Spectrum Mgmt) + */ + printf(" %c%c%c%c%c%c%c%c%c%c | ", bss_info->cap_info.ibss ? 'A' : 'I', bss_info->priv_cap, /* P (WEP), W (WPA), 2 (WPA2) */ + bss_info->cap_info.spectrum_mgmt ? 'D' : ' ', bss_info->wmm_cap, /* M (WMM), C (WMM-Call Admission Control) */ + bss_info->dot11k_cap, /* K */ + bss_info->dot11r_cap, /* R */ + bss_info->wps_cap, /* S */ + bss_info->ht_cap, /* N */ + bss_info->vht_cap[0], /* AC */ + bss_info->vht_cap[1]); + + /* Print out the ssid or the hex values if non-printable */ + for (ssid_idx = 0; ssid_idx < bss_info->ssid_len; ssid_idx++) { + if (isprint(bss_info->ssid[ssid_idx])) { + printf("%c", bss_info->ssid[ssid_idx]); + } else { + printf("\\%02x", bss_info->ssid[ssid_idx]); + } + } + + printf("\n"); + + if (argc > 3 && strcmp(argv[3], "tsf") == 0) { + /* TSF is a u64, some formatted printing libs have trouble + printing long longs, so cast and dump as bytes */ + pbyte = (t_u8 *)&fixed_fields.network_tsf; + printf(" TSF=%02x%02x%02x%02x%02x%02x%02x%02x\n", + pbyte[7], pbyte[6], pbyte[5], pbyte[4], + pbyte[3], pbyte[2], pbyte[1], pbyte[0]); + } + } + + if (displayed_info == TRUE) { + if (argc > 3 && strcmp(argv[3], "help") == 0) { + printf("\n\n" + "Capability Legend (Not all may be supported)\n" + "-----------------\n" + " I [ Infrastructure ]\n" + " A [ Ad-hoc ]\n" + " W [ WPA IE ]\n" + " 2 [ WPA2/RSN IE ]\n" + " M [ WMM IE ]\n" + " C [ Call Admission Control - WMM IE, VO ACM set ]\n" + " D [ Spectrum Management - DFS (11h) ]\n" + " K [ 11k ]\n" + " R [ 11r ]\n" + " S [ WPS ]\n" + " N [ HT (11n) ]\n" + " AC [VHT (11ac) ]\n" "\n\n"); + } + } else { + printf("< No Scan Results >\n"); + } + +done: + if (scan_rsp_buf) + free(scan_rsp_buf); + for (curr = scan_list_head; curr != NULL; curr = next) { + next = curr->next; + free(curr); + } + return ret; +} + +/** Maximum channel scratch */ +#define MAX_CHAN_SCRATCH 100 + +/** Maximum channel number for b/g band */ +#define MAX_CHAN_BG_BAND 14 + +/** Maximum number of probes to send on each channel */ +#define MAX_PROBES 4 + +/** Scan all the channels in specified band */ +#define BAND_SPECIFIED 0x80 +/** + * @brief Request a scan from the driver and display the scan table afterwards + * + * Command line interface for performing a specific immediate scan based + * on the following keyword parsing: + * + * chan=[chan#][band][mode] where band is [a,b,g,n] and mode is + * blank for active or 'p' for passive + * bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + * ssid="[SSID]" specify a SSID filter for the scan + * keep=[0 or 1] keep the previous scan results (1), discard (0) + * dur=[scan time] time to scan for each channel in milliseconds + * probes=[#] number of probe requests to send on each chan + * type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + * + * Any combination of the above arguments can be supplied on the command line. + * If the chan token is absent, a full channel scan will be completed by + * the driver. If the dur or probes tokens are absent, the drivers default + * setting will be used. The bssid and ssid fields, if blank, + * will produce an unfiltered scan. The type field will default to 3 (Any) + * and the keep field will default to 0 (Discard). + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_setuserscan(int argc, char *argv[]) +{ + wlan_ioctl_user_scan_cfg scan_req; + int ioctl_val, subioctl_val; + struct iwreq iwr; + char *parg_tok; + char *pchan_tok; + char *parg_cookie; + char *pchan_cookie; + int arg_idx; + int chan_parse_idx; + int chan_cmd_idx; + char chan_scratch[MAX_CHAN_SCRATCH]; + char *pscratch; + int tmp_idx; + int scan_time; + int num_ssid; + int is_radio_set; + unsigned int mac[ETH_ALEN]; + + memset(&scan_req, 0x00, sizeof(scan_req)); + chan_cmd_idx = 0; + scan_time = 0; + num_ssid = 0; + + if (get_priv_ioctl("setuserscan", + &ioctl_val, &subioctl_val) == MLAN_STATUS_FAILURE) { + return -EOPNOTSUPP; + } + + for (arg_idx = 0; arg_idx < argc; arg_idx++) { + + if (strncmp(argv[arg_idx], "ssid=", strlen("ssid=")) == 0) { + /* + * "ssid" token string handler + */ + if (num_ssid < MRVDRV_MAX_SSID_LIST_LENGTH) { + strncpy(scan_req.ssid_list[num_ssid].ssid, + argv[arg_idx] + strlen("ssid="), + sizeof(scan_req.ssid_list[num_ssid]. + ssid)); + + scan_req.ssid_list[num_ssid].max_len = 0; + + num_ssid++; + } + } else if (strncmp(argv[arg_idx], "bssid=", strlen("bssid=")) == + 0) { + /* + * "bssid" token string handler + */ + sscanf(argv[arg_idx] + strlen("bssid="), + "%2x:%2x:%2x:%2x:%2x:%2x", mac + 0, mac + 1, + mac + 2, mac + 3, mac + 4, mac + 5); + + for (tmp_idx = 0; + (unsigned int)tmp_idx < NELEMENTS(mac); + tmp_idx++) { + scan_req.specific_bssid[tmp_idx] = + (t_u8)mac[tmp_idx]; + } + } else if (strncmp(argv[arg_idx], "chan=", strlen("chan=")) == + 0) { + /* + * "chan" token string handler + */ + parg_tok = argv[arg_idx] + strlen("chan="); + + if (strlen(parg_tok) > MAX_CHAN_SCRATCH) { + printf("Error: Specified channels exceeds max limit\n"); + return MLAN_STATUS_FAILURE; + } + is_radio_set = FALSE; + + while ((parg_tok = + strtok_r(parg_tok, ",", + &parg_cookie)) != NULL) { + + memset(chan_scratch, 0x00, + sizeof(chan_scratch)); + pscratch = chan_scratch; + + for (chan_parse_idx = 0; + (unsigned int)chan_parse_idx < + strlen(parg_tok); chan_parse_idx++) { + if (isalpha + (*(parg_tok + chan_parse_idx))) { + *pscratch++ = ' '; + } + + *pscratch++ = + *(parg_tok + chan_parse_idx); + } + *pscratch = 0; + parg_tok = NULL; + + pchan_tok = chan_scratch; + + while ((pchan_tok = strtok_r(pchan_tok, " ", + &pchan_cookie)) != + NULL) { + if (isdigit(*pchan_tok)) { + scan_req. + chan_list[chan_cmd_idx]. + chan_number = + atoi(pchan_tok); + if (scan_req. + chan_list[chan_cmd_idx]. + chan_number > + MAX_CHAN_BG_BAND) + scan_req. + chan_list + [chan_cmd_idx]. + radio_type = 1; + } else { + switch (toupper(*pchan_tok)) { + case 'A': + scan_req. + chan_list + [chan_cmd_idx]. + radio_type = 1; + is_radio_set = TRUE; + break; + case 'B': + case 'G': + scan_req. + chan_list + [chan_cmd_idx]. + radio_type = 0; + is_radio_set = TRUE; + break; + case 'N': + break; + case 'P': + scan_req. + chan_list + [chan_cmd_idx]. + scan_type = + MLAN_SCAN_TYPE_PASSIVE; + break; + default: + printf("Error: Band type not supported!\n"); + return -EOPNOTSUPP; + } + if (!chan_cmd_idx && + !scan_req. + chan_list[chan_cmd_idx]. + chan_number && is_radio_set) + scan_req. + chan_list + [chan_cmd_idx]. + radio_type |= + BAND_SPECIFIED; + } + pchan_tok = NULL; + } + chan_cmd_idx++; + } + } else if (strncmp(argv[arg_idx], "keep=", strlen("keep=")) == + 0) { + /* + * "keep" token string handler + */ + scan_req.keep_previous_scan = + atoi(argv[arg_idx] + strlen("keep=")); + } else if (strncmp(argv[arg_idx], "dur=", strlen("dur=")) == 0) { + /* + * "dur" token string handler + */ + scan_time = atoi(argv[arg_idx] + strlen("dur=")); + scan_req.chan_list[0].scan_time = scan_time; + + } else if (strncmp(argv[arg_idx], "wc=", strlen("wc=")) == 0) { + + if (num_ssid < MRVDRV_MAX_SSID_LIST_LENGTH) { + /* + * "wc" token string handler + */ + pscratch = strrchr(argv[arg_idx], ','); + + if (pscratch) { + *pscratch = 0; + pscratch++; + + if (isdigit(*pscratch)) { + scan_req.ssid_list[num_ssid]. + max_len = + atoi(pscratch); + } else { + scan_req.ssid_list[num_ssid]. + max_len = *pscratch; + } + } else { + /* Standard wildcard matching */ + scan_req.ssid_list[num_ssid].max_len = + 0xFF; + } + + strncpy(scan_req.ssid_list[num_ssid].ssid, + argv[arg_idx] + strlen("wc="), + sizeof(scan_req.ssid_list[num_ssid]. + ssid)); + + num_ssid++; + } + } else if (strncmp(argv[arg_idx], "probes=", strlen("probes=")) + == 0) { + /* + * "probes" token string handler + */ + scan_req.num_probes = + atoi(argv[arg_idx] + strlen("probes=")); + if (scan_req.num_probes > MAX_PROBES) { + fprintf(stderr, "Invalid probes (> %d)\n", + MAX_PROBES); + return -EOPNOTSUPP; + } + } else if (strncmp(argv[arg_idx], "type=", strlen("type=")) == + 0) { + /* + * "type" token string handler + */ + scan_req.bss_mode = + atoi(argv[arg_idx] + strlen("type=")); + switch (scan_req.bss_mode) { + case MLAN_SCAN_MODE_BSS: + case MLAN_SCAN_MODE_IBSS: + break; + case MLAN_SCAN_MODE_ANY: + default: + /* Set any unknown types to ANY */ + scan_req.bss_mode = MLAN_SCAN_MODE_ANY; + break; + } + } + } + + /* + * Update all the channels to have the same scan time + */ + for (tmp_idx = 1; tmp_idx < chan_cmd_idx; tmp_idx++) { + scan_req.chan_list[tmp_idx].scan_time = scan_time; + } + + strncpy(iwr.ifr_name, dev_name, IFNAMSIZ - 1); + iwr.u.data.pointer = (caddr_t) & scan_req; + iwr.u.data.length = sizeof(scan_req); + iwr.u.data.flags = subioctl_val; + + if (ioctl(sockfd, ioctl_val, &iwr) < 0) { + perror("mlanconfig: setuserscan ioctl"); + return -EFAULT; + } + + process_getscantable(0, 0); + + return MLAN_STATUS_SUCCESS; +}
diff --git a/wlan_sd8897/mapp/mlanconfig/mlanmisc.h b/wlan_sd8897/mapp/mlanconfig/mlanmisc.h new file mode 100644 index 0000000..076ce6b --- /dev/null +++ b/wlan_sd8897/mapp/mlanconfig/mlanmisc.h
@@ -0,0 +1,683 @@ +/** @file mlanmisc.h + * + * @brief This file contains command definitions for application + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 03/10/2009: initial version +************************************************************************/ + +#ifndef _MLANMISC_H_ +#define _MLANMISC_H_ + +/** Maximum size of IEEE Information Elements */ +#define IEEE_MAX_IE_SIZE 256 + +/** Maximum scan response buffer size */ +#define SCAN_RESP_BUF_SIZE 2000 + +#ifdef FALSE +#undef FALSE +#endif + +#ifdef TRUE +#undef TRUE +#endif + +#ifndef MIN +/** Find minimum value */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +#ifndef MAX +/** Find maximum value */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif /* MAX */ + +/** Type enumeration of WMM AC_QUEUES */ +typedef enum _mlan_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} __ATTRIB_PACK__ mlan_wmm_ac_e; + +/** Maximum length of SSID */ +#define MRVDRV_MAX_SSID_LENGTH 32 + +/** Enumeration for scan mode */ +enum { + MLAN_SCAN_MODE_UNCHANGED = 0, + MLAN_SCAN_MODE_BSS, + MLAN_SCAN_MODE_IBSS, + MLAN_SCAN_MODE_ANY +}; + +/** Enumeration for scan type */ +enum { + MLAN_SCAN_TYPE_UNCHANGED = 0, + MLAN_SCAN_TYPE_ACTIVE, + MLAN_SCAN_TYPE_PASSIVE +}; + +/** Length of ethernet address */ +#ifndef ETH_ALEN +#define ETH_ALEN 6 +#endif +/** Maximum length of SSID list */ +#define MRVDRV_MAX_SSID_LIST_LENGTH 10 + +/** Maximum number of channels that can be sent in a setuserscan ioctl */ +#define WLAN_IOCTL_USER_SCAN_CHAN_MAX 50 + +/** IEEE Type definitions */ +typedef enum _IEEEtypes_ElementId_e { + SSID = 0, + SUPPORTED_RATES = 1, + FH_PARAM_SET = 2, + DS_PARAM_SET = 3, + CF_PARAM_SET = 4, + + IBSS_PARAM_SET = 6, + + COUNTRY_INFO = 7, + + POWER_CONSTRAINT = 32, + POWER_CAPABILITY = 33, + TPC_REQUEST = 34, + TPC_REPORT = 35, + SUPPORTED_CHANNELS = 36, + CHANNEL_SWITCH_ANN = 37, + QUIET = 40, + IBSS_DFS = 41, + HT_CAPABILITY = 45, + HT_OPERATION = 61, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + EXT_CAPABILITY = 127, + + VHT_CAPABILITY = 191, + VHT_OPERATION = 192, + EXT_BSS_LOAD = 193, + BW_CHANNEL_SWITCH = 194, + VHT_TX_POWER_ENV = 195, + EXT_POWER_CONSTR = 196, + AID_INFO = 197, + QUIET_CHAN = 198, + + ERP_INFO = 42, + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + WMM_IE = VENDOR_SPECIFIC_221, + + WPS_IE = VENDOR_SPECIFIC_221, + + WPA_IE = VENDOR_SPECIFIC_221, + RSN_IE = 48, +} __ATTRIB_PACK__ IEEEtypes_ElementId_e; + +/** Capability Bit Map*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef struct _IEEEtypes_CapInfo_t { + t_u8 rsrvd1:2; + t_u8 dsss_ofdm:1; + t_u8 rsvrd2:2; + t_u8 short_slot_time:1; + t_u8 rsrvd3:1; + t_u8 spectrum_mgmt:1; + t_u8 chan_agility:1; + t_u8 pbcc:1; + t_u8 short_preamble:1; + t_u8 privacy:1; + t_u8 cf_poll_rqst:1; + t_u8 cf_pollable:1; + t_u8 ibss:1; + t_u8 ess:1; +} __ATTRIB_PACK__ IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#else +typedef struct _IEEEtypes_CapInfo_t { + /** Capability Bit Map : ESS */ + t_u8 ess:1; + /** Capability Bit Map : IBSS */ + t_u8 ibss:1; + /** Capability Bit Map : CF pollable */ + t_u8 cf_pollable:1; + /** Capability Bit Map : CF poll request */ + t_u8 cf_poll_rqst:1; + /** Capability Bit Map : privacy */ + t_u8 privacy:1; + /** Capability Bit Map : Short preamble */ + t_u8 short_preamble:1; + /** Capability Bit Map : PBCC */ + t_u8 pbcc:1; + /** Capability Bit Map : Channel agility */ + t_u8 chan_agility:1; + /** Capability Bit Map : Spectrum management */ + t_u8 spectrum_mgmt:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd3:1; + /** Capability Bit Map : Short slot time */ + t_u8 short_slot_time:1; + /** Capability Bit Map : APSD */ + t_u8 apsd:1; + /** Capability Bit Map : Reserved */ + t_u8 rsvrd2:1; + /** Capability Bit Map : DSS OFDM */ + t_u8 dsss_ofdm:1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd1:2; +} __ATTRIB_PACK__ IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#endif /* BIG_ENDIAN_SUPPORT */ + +/** IEEE IE header */ +typedef struct _IEEEtypes_Header_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; +} __ATTRIB_PACK__ IEEEtypes_Header_t, *pIEEEtypes_Header_t; + +/** IEEE IE header */ +#define IEEE_HEADER_LEN sizeof(IEEEtypes_Header_t) + +/** Vendor specific IE header */ +typedef struct _IEEEtypes_VendorHeader_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** OUI */ + t_u8 oui[3]; + /** OUI type */ + t_u8 oui_type; + /** OUI subtype */ + t_u8 oui_subtype; + /** Version */ + t_u8 version; +} __ATTRIB_PACK__ IEEEtypes_VendorHeader_t, *pIEEEtypes_VendorHeader_t; + +/** Vendor specific IE */ +typedef struct _IEEEtypes_VendorSpecific_t { + /** Vendor specific IE header */ + IEEEtypes_VendorHeader_t vend_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_VendorHeader_t)]; +} __ATTRIB_PACK__ IEEEtypes_VendorSpecific_t, *pIEEEtypes_VendorSpecific_t; + +/** IEEE IE */ +typedef struct _IEEEtypes_Generic_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_Header_t)]; +} __ATTRIB_PACK__ IEEEtypes_Generic_t, *pIEEEtypes_Generic_t; + +/** Size of a TSPEC. Used to allocate necessary buffer space in commands */ +#define WMM_TSPEC_SIZE 63 + +/** Maximum number of AC QOS queues available in the driver/firmware */ +#define MAX_AC_QUEUES 4 + +/** Maximum number of User Priorities */ +#define MAX_USER_PRIORITIES 8 + +/** Extra IE bytes allocated in messages for appended IEs after a TSPEC */ +#define WMM_ADDTS_EXTRA_IE_BYTES 256 + +/** + * @brief Enumeration for the command result from an ADDTS or DELTS command + */ +typedef enum { + TSPEC_RESULT_SUCCESS = 0, + TSPEC_RESULT_EXEC_FAILURE = 1, + TSPEC_RESULT_TIMEOUT = 2, + TSPEC_RESULT_DATA_INVALID = 3, +} __ATTRIB_PACK__ mlan_wmm_tspec_result_e; + +/** + * @brief Enumeration for the action field in the Queue configure command + */ +typedef enum { + WMM_QUEUE_CONFIG_ACTION_GET = 0, + WMM_QUEUE_CONFIG_ACTION_SET = 1, + WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2, + + WMM_QUEUE_CONFIG_ACTION_MAX +} __ATTRIB_PACK__ mlan_wmm_queue_config_action_e; + +/** + * @brief Enumeration for the action field in the queue stats command + */ +typedef enum { + WMM_STATS_ACTION_START = 0, + WMM_STATS_ACTION_STOP = 1, + WMM_STATS_ACTION_GET_CLR = 2, + WMM_STATS_ACTION_SET_CFG = 3, /* Not currently used */ + WMM_STATS_ACTION_GET_CFG = 4, /* Not currently used */ + + WMM_STATS_ACTION_MAX +} __ATTRIB_PACK__ mlan_wmm_stats_action_e; + +/** Data structure of WMM Aci/Aifsn */ +typedef struct _IEEEtypes_WmmAciAifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved:1; + /** Aci */ + t_u8 aci:2; + /** Acm */ + t_u8 acm:1; + /** Aifsn */ + t_u8 aifsn:4; +#else + /** Aifsn */ + t_u8 aifsn:4; + /** Acm */ + t_u8 acm:1; + /** Aci */ + t_u8 aci:2; + /** Reserved */ + t_u8 reserved:1; +#endif +} __ATTRIB_PACK__ IEEEtypes_WmmAciAifsn_t, *pIEEEtypes_WmmAciAifsn_t; + +/** Data structure of WMM ECW */ +typedef struct _IEEEtypes_WmmEcw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max:4; + /** Minimum Ecw */ + t_u8 ecw_min:4; +#else + /** Minimum Ecw */ + t_u8 ecw_min:4; + /** Maximum Ecw */ + t_u8 ecw_max:4; +#endif +} __ATTRIB_PACK__ IEEEtypes_WmmEcw_t, *pIEEEtypes_WmmEcw_t; + +/** Data structure of WMM AC parameters */ +typedef struct _IEEEtypes_WmmAcParameters_t { + IEEEtypes_WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + IEEEtypes_WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} __ATTRIB_PACK__ IEEEtypes_WmmAcParameters_t, *pIEEEtypes_WmmAcParameters_t; + +/** Data structure of WMM Info IE */ +typedef struct _IEEEtypes_WmmInfo_t { + + /** + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + +} __ATTRIB_PACK__ IEEEtypes_WmmInfo_t, *pIEEEtypes_WmmInfo_t; + +/** Data structure of WMM parameter IE */ +typedef struct _IEEEtypes_WmmParameter_t { + /** + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + /** Reserved */ + t_u8 reserved; + + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + IEEEtypes_WmmAcParameters_t ac_params[MAX_AC_QUEUES]; + +} __ATTRIB_PACK__ IEEEtypes_WmmParameter_t, *pIEEEtypes_WmmParameter_t; + +/** + * @brief IOCTL structure to send an ADDTS request and retrieve the response. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an ADDTS management frame with an appropriate TSPEC IE as well + * as any additional IEs appended in the ADDTS Action frame. + * + * @sa wlan_wmm_addts_req_ioctl + */ +typedef struct { + mlan_wmm_tspec_result_e commandResult; + /**< Firmware execution result */ + + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 ieeeStatusCode; /**< IEEE status code */ + + t_u32 ieDataLen; + t_u8 ieData[WMM_TSPEC_SIZE + /**< TSPEC to send in the ADDTS */ + + WMM_ADDTS_EXTRA_IE_BYTES]; + /**< ADDTS extra IE buf */ +} wlan_ioctl_wmm_addts_req_t; + +/** + * @brief IOCTL structure to send a DELTS request. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an DELTS management frame with an appropriate TSPEC IE. + * + * @sa wlan_wmm_delts_req_ioctl + */ +typedef struct { + mlan_wmm_tspec_result_e commandResult; + /**< Firmware execution result */ + t_u8 ieeeReasonCode; /**< IEEE reason code sent, unused for WMM */ + + t_u32 ieDataLen; + t_u8 ieData[WMM_TSPEC_SIZE]; + /**< TSPEC to send in the DELTS */ +} wlan_ioctl_wmm_delts_req_t; + +/** + * @brief IOCTL structure to configure a specific AC Queue's parameters + * + * IOCTL structure from the application layer relayed to firmware to + * get, set, or default the WMM AC queue parameters. + * + * - msduLifetimeExpiry is ignored if set to 0 on a set command + * + * @sa wlan_wmm_queue_config_ioctl + */ +typedef struct { + mlan_wmm_queue_config_action_e action; + /**< Set, Get, or Default */ + mlan_wmm_ac_e accessCategory; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + t_u16 msduLifetimeExpiry; /**< lifetime expiry in TUs */ + t_u8 supportedRates[10]; /**< Not supported yet */ +} wlan_ioctl_wmm_queue_config_t; + +/** Number of bins in the histogram for the HostCmd_DS_WMM_QUEUE_STATS */ +#define WMM_STATS_PKTS_HIST_BINS 7 + +/** + * @brief IOCTL structure to start, stop, and get statistics for a WMM AC + * + * IOCTL structure from the application layer relayed to firmware to + * start or stop statistical collection for a given AC. Also used to + * retrieve and clear the collected stats on a given AC. + * + * @sa wlan_wmm_queue_stats_ioctl + */ +typedef struct { + mlan_wmm_stats_action_e action; + /**< Start, Stop, or Get */ + t_u8 userPriority; + /**< User Priority (0 to 7) */ + t_u16 pktCount; /**< Number of successful packets transmitted */ + t_u16 pktLoss; /**< Packets lost; not included in pktCount */ + t_u32 avgQueueDelay; + /**< Average Queue delay in microseconds */ + t_u32 avgTxDelay;/**< Average Transmission delay in microseconds */ + t_u16 usedTime; /**< Calculated used time - units of 32 microsec */ + t_u16 policedTime; + /**< Calculated policed time - units of 32 microsec */ + + /** @brief Queue Delay Histogram; number of packets per queue delay range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delayHistogram[WMM_STATS_PKTS_HIST_BINS]; +} wlan_ioctl_wmm_queue_stats_t; + +/** + * @brief IOCTL and command sub structure for a Traffic stream status. + */ +typedef struct { + t_u8 tid; /**< TSID: Range: 0->7 */ + t_u8 valid; /**< TSID specified is valid */ + t_u8 accessCategory;/**< AC TSID is active on */ + t_u8 userPriority; /**< UP specified for the TSID */ + + t_u8 psb; /**< Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 flowDir; /**< Upstream (0), Downlink(1), Bidirectional(3) */ + t_u16 mediumTime; /**< Medium time granted for the TSID */ +} __ATTRIB_PACK__ HostCmd_DS_WMM_TS_STATUS, + wlan_ioctl_wmm_ts_status_t, wlan_cmd_wmm_ts_status_t; + +/** + * @brief IOCTL sub structure for a specific WMM AC Status + */ +typedef struct { + /** WMM Acm */ + t_u8 wmmAcm; + /** Flow required flag */ + t_u8 flowRequired; + /** Flow created flag */ + t_u8 flowCreated; + /** Disabled flag */ + t_u8 disabled; + /** delivery enabled */ + t_u8 deliveryEnabled; + /** trigger enabled */ + t_u8 triggerEnabled; +} wlan_ioctl_wmm_queue_status_ac_t; + +/** + * @brief IOCTL structure to retrieve the WMM AC Queue status + * + * IOCTL structure from the application layer to retrieve: + * - ACM bit setting for the AC + * - Firmware status (flow required, flow created, flow disabled) + * + * @sa wlan_wmm_queue_status_ioctl + */ +typedef struct { + /** WMM AC queue status */ + wlan_ioctl_wmm_queue_status_ac_t acStatus[MAX_AC_QUEUES]; +} wlan_ioctl_wmm_queue_status_t; + +typedef struct _wlan_get_scan_table_fixed { + /** BSSID of this network */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Channel this beacon/probe response was detected */ + t_u8 channel; + /** RSSI for the received packet */ + t_u8 rssi; + /** TSF value from the firmware at packet reception */ + t_u64 network_tsf; +} wlan_get_scan_table_fixed; + +/** + * Structure passed in the wlan_ioctl_get_scan_table_info for each + * BSS returned in the WLAN_GET_SCAN_RESP IOCTL + */ +typedef struct _wlan_ioctl_get_scan_table_entry { + /** + * Fixed field length included in the response. + * + * Length value is included so future fixed fields can be added to the + * response without breaking backwards compatibility. Use the length + * to find the offset for the bssInfoLength field, not a sizeof() calc. + */ + t_u32 fixed_field_length; + + /** + * Length of the BSS Information (probe resp or beacon) that + * follows after the fixed_field_length + */ + t_u32 bss_info_length; + + /** + * Always present, fixed length data fields for the BSS + */ + wlan_get_scan_table_fixed fixed_fields; + + /* + * Probe response or beacon scanned for the BSS. + * + * Field layout: + * - TSF 8 octets + * - Beacon Interval 2 octets + * - Capability Info 2 octets + * + * - IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /* t_u8 bss_info_buffer[1]; */ +} wlan_ioctl_get_scan_table_entry; + +/** + * Structure to store BSS info (probe resp or beacon) & IEEE IE info for each + * BSS returned in WLAN_GET_SCAN_RESP IOCTL + */ +typedef struct _wlan_ioctl_get_bss_info { + /** + * Length of the BSS Information (probe resp or beacon) that + * follows after the fixed_field + */ + t_u32 bss_info_length; + + /** + * Probe response or beacon scanned for the BSS. + * + * Field layout: + */ + /** TSF 8 octets */ + t_u8 tsf[8]; + /** Beacon Interval 2 octets */ + t_u16 beacon_interval; + /** Capability Info 2 octets */ + IEEEtypes_CapInfo_t cap_info; + + /** + * IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /** SSID */ + char ssid[MRVDRV_MAX_SSID_LENGTH + 1]; + /** SSID Length */ + t_u32 ssid_len; + /** WMM Capability */ + char wmm_cap; + /** WPS Capability */ + char wps_cap; + /** Privacy Capability - WEP/WPA/RSN */ + char priv_cap; + /** HT (11N) Capability */ + char ht_cap; + /** VHT (11AC) Capability */ + char vht_cap[2]; + /* 802.11k Capability */ + char dot11k_cap; + /** 802.11r Capability */ + char dot11r_cap; +} wlan_ioctl_get_bss_info; + +/** + * Structure to save of scan table info for each BSS returned + * in WLAN_GET_SCAN_RESP IOCTL + */ +struct wlan_ioctl_get_scan_list { + /** fixed info */ + wlan_ioctl_get_scan_table_entry fixed_buf; + /** variable info - BSS info (probe resp or beacon) & IEEE IE info */ + wlan_ioctl_get_bss_info bss_info_buf; + /** pointer to next node in list */ + struct wlan_ioctl_get_scan_list *next; +}; + +/** + * Sructure to retrieve the scan table + */ +typedef struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry structures. + * Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; + +} wlan_ioctl_get_scan_table_info; + +typedef struct { + t_u8 chan_number; + /**< Channel Number to scan */ + t_u8 radio_type; + /**< Radio type: 'B/G' Band = 0, 'A' Band = 1 */ + t_u8 scan_type;/**< Scan type: Active = 1, Passive = 2 */ + t_u8 reserved;/**< Reserved */ + t_u32 scan_time; + /**< Scan duration in milliseconds; if 0 default used */ +} __ATTRIB_PACK__ wlan_ioctl_user_scan_chan; + +typedef struct { + char ssid[MRVDRV_MAX_SSID_LENGTH + 1]; + /**< SSID */ + t_u8 max_len; /**< Maximum length of SSID */ +} __ATTRIB_PACK__ wlan_ioctl_user_scan_ssid; + +typedef struct { + + /** Flag set to keep the previous scan table intact */ + t_u8 keep_previous_scan; /* Do not erase the existing scan results */ + + /** BSS mode to be sent in the firmware command */ + t_u8 bss_mode; + + /** Configure the number of probe requests for active chan scans */ + t_u8 num_probes; + + /** Reserved */ + t_u8 reserved; + + /** BSSID filter sent in the firmware command to limit the results */ + t_u8 specific_bssid[ETH_ALEN]; + + /** SSID filter list used in the to limit the scan results */ + wlan_ioctl_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + + /** Variable number (fixed maximum) of channels to scan up */ + wlan_ioctl_user_scan_chan chan_list[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; + +} __ATTRIB_PACK__ wlan_ioctl_user_scan_cfg; + +int process_sdcmd52rw(int argc, char *argv[]); +int process_sdcmd53rw(int argc, char *argv[]); +int process_setuserscan(int argc, char *argv[]); +int process_getscantable(int argc, char *argv[]); +int process_getscantable_idx(wlan_ioctl_get_scan_table_info *prsp_info_req); + +#endif /* _MLANMISC_H_ */
diff --git a/wlan_sd8897/mapp/mlanevent/Makefile b/wlan_sd8897/mapp/mlanevent/Makefile new file mode 100644 index 0000000..ae2a463 --- /dev/null +++ b/wlan_sd8897/mapp/mlanevent/Makefile
@@ -0,0 +1,51 @@ +# File : mlanevent/Makefile +# +# Copyright (C) 2008-2017, Marvell International Ltd. All Rights Reserved + +# Path to the top directory of the wlan distribution +PATH_TO_TOP = ../.. + +# Determine how we should copy things to the install directory +ABSPATH := $(filter /%, $(INSTALLDIR)) +RELPATH := $(filter-out /%, $(INSTALLDIR)) +INSTALLPATH := $(ABSPATH) +ifeq ($(strip $(INSTALLPATH)),) +INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH) +endif + +# Override CFLAGS for application sources, remove __ kernel namespace defines +CFLAGS := $(filter-out -D__%, $(ccflags-y)) +# remove KERNEL include dir +CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS)) + + +#CFLAGS += -DAP22 -fshort-enums +CFLAGS += -Wall +#ECHO = @ +LIBS = -lrt + +.PHONY: default tags all + +OBJECTS = mlanevent.o +HEADERS = mlanevent.h + +TARGET = mlanevent.exe + +build default: $(TARGET) + @cp -f $(TARGET) $(INSTALLPATH) + +all : tags default + +$(TARGET): $(OBJECTS) $(HEADERS) + $(ECHO)$(CC) $(LIBS) -o $@ $(OBJECTS) + +%.o: %.c $(HEADERS) + $(ECHO)$(CC) $(CFLAGS) -c -o $@ $< + +tags: + ctags -R -f tags.txt + +distclean clean: + $(ECHO)$(RM) $(OBJECTS) $(TARGET) + $(ECHO)$(RM) tags.txt +
diff --git a/wlan_sd8897/mapp/mlanevent/mlanevent.c b/wlan_sd8897/mapp/mlanevent/mlanevent.c new file mode 100644 index 0000000..5397f54 --- /dev/null +++ b/wlan_sd8897/mapp/mlanevent/mlanevent.c
@@ -0,0 +1,2716 @@ +/** @file mlanevent.c + * + * @brief Program to receive events from the driver/firmware of the uAP + * driver. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/**************************************************************************** +Change log: + 03/18/08: Initial creation +****************************************************************************/ + +/**************************************************************************** + Header files +****************************************************************************/ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <signal.h> +#include <time.h> +#include <sys/time.h> +#include <getopt.h> + +#include <sys/socket.h> +#include <linux/netlink.h> +#include <linux/if.h> +#include "mlanevent.h" +#ifdef WIFI_DIRECT_SUPPORT +#include <arpa/inet.h> +#endif + +/**************************************************************************** + Definitions +****************************************************************************/ +/** Enable or disable debug outputs */ +#define DEBUG 0 + +/**************************************************************************** + Global variables +****************************************************************************/ +/** Termination flag */ +int terminate_flag = 0; + +/**************************************************************************** + Local functions +****************************************************************************/ +/** + * @brief Signal handler + * + * @param sig Received signal number + * @return N/A + */ +void +sig_handler(int sig) +{ + printf("Stopping application.\n"); +#if DEBUG + printf("Process ID of process killed = %d\n", getpid()); +#endif + terminate_flag = 1; +} + +/** + * @brief Dump hex data + * + * @param p A pointer to data buffer + * @param len The len of data buffer + * @param delim Deliminator character + * @return Hex integer + */ +static void +hexdump(void *p, t_s32 len, char delim) +{ + t_s32 i; + t_u8 *s = p; + for (i = 0; i < len; i++) { + if (i != len - 1) + printf("%02x%c", *s++, delim); + else + printf("%02x\n", *s); + if ((i + 1) % 16 == 0) + printf("\n"); + } +} + +/** + * @brief Hex to number + * + * @param c Hex value + * @return Integer value or -1 + */ +int +hex2num(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; +} + +/** + * @brief Check hex string + * + * @param hex A pointer to hex string + * @return 0 or MLAN_EVENT_FAILURE + */ +int +ishexstring(void *hex) +{ + int i, a; + char *p = hex; + int len = strlen(p); + if (!strncasecmp("0x", p, 2)) { + p += 2; + len -= 2; + } + for (i = 0; i < len; i++) { + a = hex2num(*p); + if (a < 0) + return MLAN_EVENT_FAILURE; + p++; + } + return 0; +} + +/** + * @brief Convert char to hex integer + * + * @param chr Char + * @return Hex integer + */ +unsigned char +hexc2bin(char chr) +{ + if (chr >= '0' && chr <= '9') + chr -= '0'; + else if (chr >= 'A' && chr <= 'F') + chr -= ('A' - 10); + else if (chr >= 'a' && chr <= 'f') + chr -= ('a' - 10); + + return chr; +} + +/** + * @brief Convert string to hex integer + * + * @param s A pointer string buffer + * @return Hex integer + */ +unsigned int +a2hex(char *s) +{ + unsigned int val = 0; + if (!strncasecmp("0x", s, 2)) { + s += 2; + } + while (*s && isxdigit(*s)) { + val = (val << 4) + hexc2bin(*s++); + } + return val; +} + +/** + * @brief Prints a MAC address in colon separated form from raw data + * + * @param raw A pointer to the hex data buffer + * @return N/A + */ +void +print_mac(t_u8 *raw) +{ + printf("%02x:%02x:%02x:%02x:%02x:%02x", (unsigned int)raw[0], + (unsigned int)raw[1], (unsigned int)raw[2], (unsigned int)raw[3], + (unsigned int)raw[4], (unsigned int)raw[5]); + return; +} + +/** + * @brief Print usage information + * + * @return N/A + */ +void +print_usage(void) +{ + printf("\n"); + printf("Usage : mlanevent.exe [-v] [-h]\n"); + printf(" -v : Print version information\n"); + printf(" -h : Print help information\n"); + printf(" -i : Specify device number from 0 to %d\n", + MAX_NO_OF_DEVICES - 1); + printf(" 0xff for all devices\n"); + printf("\n"); +} + +/** + * @brief Parse and print STA deauthentication event data + * + * @param buffer Pointer to received event buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_sta_deauth(t_u8 *buffer, t_u16 size) +{ + eventbuf_sta_deauth *event_body = NULL; + + if (size < sizeof(eventbuf_sta_deauth)) { + printf("ERR:Event buffer too small!\n"); + return; + } + event_body = (eventbuf_sta_deauth *)buffer; + event_body->reason_code = uap_le16_to_cpu(event_body->reason_code); + printf("EVENT: STA_DEAUTH\n"); + printf("Deauthenticated STA MAC: "); + print_mac(event_body->sta_mac_address); + printf("\nReason: "); + switch (event_body->reason_code) { + case 1: + printf("Client station leaving the network\n"); + break; + case 2: + printf("Client station aged out\n"); + break; + case 3: + printf("Client station deauthenticated by user's request\n"); + break; + case 4: + printf("Client station authentication failure\n"); + break; + case 5: + printf("Client station association failure\n"); + break; + case 6: + printf("Client mac address is blocked by ACL filter\n"); + break; + case 7: + printf("Client station table is full\n"); + break; + case 8: + printf("Client 4-way handshake timeout\n"); + break; + case 9: + printf("Client group key handshake timeout\n"); + break; + default: + printf("Unspecified\n"); + break; + } + return; +} + +/** + * @brief Parse and print WEP ICV error event data + * + * @param buffer Pointer to received event buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_wep_icv_error(t_u8 *buffer, t_u16 size) +{ + int i = 0; + eventbuf_wep_icv_error *event_body = NULL; + + if (size < sizeof(eventbuf_wep_icv_error)) { + printf("ERR:Event buffer too small!\n"); + return; + } + event_body = (eventbuf_wep_icv_error *)buffer; + event_body->reason_code = uap_le16_to_cpu(event_body->reason_code); + printf("EVENT: WEP_ICV_ERROR\n"); + printf("Deauthenticated STA MAC: "); + print_mac(event_body->sta_mac_address); + printf("WEP key index = %d\n", event_body->wep_key_index); + printf("WEP key length = %d\n", event_body->wep_key_length); + printf("WEP key : \n"); + for (i = 0; i < event_body->wep_key_length; i++) { + printf("%02x ", event_body->wep_key[i]); + } + printf("\n"); + return; +} + +/** + * @brief Prints mgmt frame + * + * @param mgmt_tlv A pointer to mgmt_tlv + * @param tlv_len Length of tlv payload + * @return N/A + */ +void +print_mgmt_frame(MrvlIETypes_MgmtFrameSet_t *mgmt_tlv, int tlv_len) +{ + IEEEtypes_AssocRqst_t *assoc_req = NULL; + IEEEtypes_ReAssocRqst_t *reassoc_req = NULL; + IEEEtypes_AssocRsp_t *assoc_resp = NULL; + t_u16 frm_ctl = 0; + printf("\nMgmt Frame:\n"); + memcpy(&frm_ctl, &mgmt_tlv->frame_control, sizeof(t_u16)); + printf("FrameControl: 0x%x\n", frm_ctl); + if (mgmt_tlv->frame_control.type != 0) { + printf("Frame type=%d subtype=%d:\n", + mgmt_tlv->frame_control.type, + mgmt_tlv->frame_control.sub_type); + hexdump(mgmt_tlv->frame_contents, tlv_len - sizeof(t_u16), ' '); + return; + } + switch (mgmt_tlv->frame_control.sub_type) { + case SUBTYPE_ASSOC_REQUEST: + printf("Assoc Request:\n"); + assoc_req = (IEEEtypes_AssocRqst_t *)mgmt_tlv->frame_contents; + printf("CapInfo: 0x%x ListenInterval: 0x%x \n", + uap_le16_to_cpu(assoc_req->cap_info), + uap_le16_to_cpu(assoc_req->listen_interval)); + printf("AssocReqIE:\n"); + hexdump(assoc_req->ie_buffer, + tlv_len - sizeof(IEEEtypes_AssocRqst_t) + - sizeof(IEEEtypes_FrameCtl_t), ' '); + break; + case SUBTYPE_REASSOC_REQUEST: + printf("ReAssoc Request:\n"); + reassoc_req = + (IEEEtypes_ReAssocRqst_t *)mgmt_tlv->frame_contents; + printf("CapInfo: 0x%x ListenInterval: 0x%x \n", + uap_le16_to_cpu(reassoc_req->cap_info), + uap_le16_to_cpu(reassoc_req->listen_interval)); + printf("Current AP address: "); + print_mac(reassoc_req->current_ap_addr); + printf("\nReAssocReqIE:\n"); + hexdump(reassoc_req->ie_buffer, + tlv_len - sizeof(IEEEtypes_ReAssocRqst_t) + - sizeof(IEEEtypes_FrameCtl_t), ' '); + break; + case SUBTYPE_ASSOC_RESPONSE: + case SUBTYPE_REASSOC_RESPONSE: + if (mgmt_tlv->frame_control.sub_type == SUBTYPE_ASSOC_RESPONSE) + printf("Assoc Response:\n"); + else + printf("ReAssoc Response:\n"); + assoc_resp = (IEEEtypes_AssocRsp_t *)mgmt_tlv->frame_contents; + printf("CapInfo: 0x%x StatusCode: %d AID: 0x%x \n", + uap_le16_to_cpu(assoc_resp->cap_info), + (int)(uap_le16_to_cpu(assoc_resp->status_code)), + uap_le16_to_cpu(assoc_resp->aid) & 0x3fff); + break; + default: + printf("Frame subtype = %d:\n", + mgmt_tlv->frame_control.sub_type); + hexdump(mgmt_tlv->frame_contents, tlv_len - sizeof(t_u16), ' '); + break; + } + return; +} + +/** + * @brief Parse and print RSN connect event data + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_rsn_connect(t_u8 *buffer, t_u16 size) +{ + int tlv_buf_left = size; + t_u16 tlv_type, tlv_len; + tlvbuf_header *tlv = NULL; + eventbuf_rsn_connect *event_body = NULL; + if (size < sizeof(eventbuf_rsn_connect)) { + printf("ERR:Event buffer too small!\n"); + return; + } + event_body = (eventbuf_rsn_connect *)buffer; + printf("EVENT: RSN_CONNECT\n"); + printf("Station MAC: "); + print_mac(event_body->sta_mac_address); + printf("\n"); + tlv_buf_left = size - sizeof(eventbuf_rsn_connect); + if (tlv_buf_left < (int)sizeof(tlvbuf_header)) + return; + tlv = (tlvbuf_header *)(buffer + sizeof(eventbuf_rsn_connect)); + + while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) { + tlv_type = uap_le16_to_cpu(tlv->type); + tlv_len = uap_le16_to_cpu(tlv->len); + if ((sizeof(tlvbuf_header) + tlv_len) > + (unsigned int)tlv_buf_left) { + printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len, + tlv_buf_left); + break; + } + switch (tlv_type) { + case IEEE_WPA_IE: + printf("WPA IE:\n"); + hexdump((t_u8 *)tlv + sizeof(tlvbuf_header), tlv_len, + ' '); + break; + case IEEE_RSN_IE: + printf("RSN IE:\n"); + hexdump((t_u8 *)tlv + sizeof(tlvbuf_header), tlv_len, + ' '); + break; + default: + printf("unknown tlv: %d\n", tlv_type); + break; + } + tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len); + tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len + + sizeof(tlvbuf_header)); + } + return; +} + +/** + * @brief Parse and print STA associate event data + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_sta_assoc(t_u8 *buffer, t_u16 size) +{ + int tlv_buf_left = size; + t_u16 tlv_type, tlv_len; + tlvbuf_header *tlv = NULL; + MrvlIEtypes_WapiInfoSet_t *wapi_tlv = NULL; + MrvlIETypes_MgmtFrameSet_t *mgmt_tlv = NULL; + eventbuf_sta_assoc *event_body = NULL; + if (size < sizeof(eventbuf_sta_assoc)) { + printf("ERR:Event buffer too small!\n"); + return; + } + event_body = (eventbuf_sta_assoc *)buffer; + printf("EVENT: STA_ASSOCIATE\n"); + printf("Associated STA MAC: "); + print_mac(event_body->sta_mac_address); + printf("\n"); + tlv_buf_left = size - sizeof(eventbuf_sta_assoc); + if (tlv_buf_left < (int)sizeof(tlvbuf_header)) + return; + tlv = (tlvbuf_header *)(buffer + sizeof(eventbuf_sta_assoc)); + + while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) { + tlv_type = uap_le16_to_cpu(tlv->type); + tlv_len = uap_le16_to_cpu(tlv->len); + if ((sizeof(tlvbuf_header) + tlv_len) > + (unsigned int)tlv_buf_left) { + printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len, + tlv_buf_left); + break; + } + switch (tlv_type) { + case MRVL_WAPI_INFO_TLV_ID: + wapi_tlv = (MrvlIEtypes_WapiInfoSet_t *)tlv; + printf("WAPI Multicast PN:\n"); + hexdump(wapi_tlv->multicast_PN, tlv_len, ' '); + break; + case MRVL_MGMT_FRAME_TLV_ID: + mgmt_tlv = (MrvlIETypes_MgmtFrameSet_t *)tlv; + print_mgmt_frame(mgmt_tlv, tlv_len); + break; + default: + printf("unknown tlv: %d\n", tlv_type); + break; + } + tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len); + tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len + + sizeof(tlvbuf_header)); + } + return; +} + +/** + * @brief Parse and print BSS start event data + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_bss_start(t_u8 *buffer, t_u16 size) +{ + eventbuf_bss_start *event_body = NULL; + int tlv_buf_left = size; + t_u16 tlv_type, tlv_len; + tlvbuf_header *tlv = NULL; + tlvbuf_channel_config *channel_tlv = NULL; + + if (size < sizeof(eventbuf_bss_start)) { + printf("ERR:Event buffer too small!\n"); + return; + } + event_body = (eventbuf_bss_start *)buffer; + printf("EVENT: BSS_START "); + printf("BSS MAC: "); + print_mac(event_body->ap_mac_address); + printf("\n"); + tlv_buf_left = size - sizeof(eventbuf_bss_start); + if (tlv_buf_left < (int)sizeof(tlvbuf_header)) + return; + tlv = (tlvbuf_header *)(buffer + sizeof(eventbuf_bss_start)); + + while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) { + tlv_type = uap_le16_to_cpu(tlv->type); + tlv_len = uap_le16_to_cpu(tlv->len); + if ((sizeof(tlvbuf_header) + tlv_len) > + (unsigned int)tlv_buf_left) { + printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len, + tlv_buf_left); + break; + } + switch (tlv_type) { + case MRVL_CHANNELCONFIG_TLV_ID: + channel_tlv = (tlvbuf_channel_config *)tlv; + printf("Channel = %d\n", channel_tlv->chan_number); + printf("Band = %s\n", + (channel_tlv->bandcfg.chanBand == + BAND_5GHZ) ? "5GHz" : "2.4GHz"); + printf("Channel Select Mode = %s\n", + (channel_tlv->bandcfg.scanMode == + SCAN_MODE_ACS) ? "ACS" : "Manual"); + if (channel_tlv->bandcfg.chan2Offset == SEC_CHAN_NONE) + printf("no secondary channel\n"); + else if (channel_tlv->bandcfg.chan2Offset == + SEC_CHAN_ABOVE) + printf("secondary channel is above primary channel\n"); + else if (channel_tlv->bandcfg.chan2Offset == + SEC_CHAN_BELOW) + printf("secondary channel is below primary channel\n"); + break; + default: +#if DEBUG + printf("unknown tlv: %d\n", tlv_type); +#endif + break; + } + tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len); + tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len + + sizeof(tlvbuf_header)); + } + + return; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Print WIFI_WPS IE elements from event payload + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_wifi_wps_ie_elements(t_u8 *buffer, t_u16 size) +{ + t_u8 *ptr = buffer; + t_u8 *array_ptr; + int i; + t_u16 wps_len = 0, wps_type = 0; + t_u16 ie_len_wps = size; + + while (ie_len_wps > sizeof(tlvbuf_wps_ie)) { + memcpy(&wps_type, ptr, sizeof(t_u16)); + memcpy(&wps_len, ptr + 2, sizeof(t_u16)); + endian_convert_tlv_wps_header_in(wps_type, wps_len); + switch (wps_type) { + case SC_Version: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + printf("\t WPS Version = 0x%2x\n", + *(wps_tlv->data)); + } + break; + case SC_Simple_Config_State: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + printf("\t WPS setupstate = 0x%x\n", + *(wps_tlv->data)); + } + break; + case SC_Request_Type: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + printf("\t WPS RequestType = 0x%x\n", + *(wps_tlv->data)); + } + break; + case SC_Response_Type: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + printf("\t WPS ResponseType = 0x%x\n", + *(wps_tlv->data)); + } + break; + case SC_Config_Methods: + { + t_u16 wps_config_methods = 0; + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + memcpy(&wps_config_methods, wps_tlv->data, + sizeof(t_u16)); + wps_config_methods = ntohs(wps_config_methods); + printf("\t WPS SpecConfigMethods = 0x%x\n", + wps_config_methods); + } + break; + case SC_UUID_E: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS UUID = "); + for (i = 0; i < wps_len; i++) + printf("0x%02X ", *array_ptr++); + printf("\n"); + } + break; + case SC_Primary_Device_Type: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS Primary Device Type = "); + for (i = 0; i < wps_len; i++) + printf("0x%02X ", *array_ptr++); + printf("\n"); + } + break; + case SC_RF_Band: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + printf("\t WPS RF Band = 0x%x\n", + *(wps_tlv->data)); + } + break; + case SC_Association_State: + { + t_u16 wps_association_state = 0; + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + memcpy(&wps_association_state, wps_tlv->data, + sizeof(t_u16)); + wps_association_state = + ntohs(wps_association_state); + printf("\t WPS Association State = 0x%x\n", + wps_association_state); + } + break; + case SC_Configuration_Error: + { + t_u16 wps_configuration_error = 0; + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + memcpy(&wps_configuration_error, wps_tlv->data, + sizeof(t_u16)); + wps_configuration_error = + ntohs(wps_configuration_error); + printf("\t WPS Configuration Error = 0x%x\n", + wps_configuration_error); + } + break; + case SC_Device_Password_ID: + { + t_u16 wps_device_password_id = 0; + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + memcpy(&wps_device_password_id, wps_tlv->data, + sizeof(t_u16)); + wps_device_password_id = + ntohs(wps_device_password_id); + printf("\t WPS Device Password ID = 0x%x\n", + wps_device_password_id); + } + break; + case SC_Device_Name: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS Device Name = "); + for (i = 0; i < wps_len; i++) + printf("%c", *array_ptr++); + printf("\n"); + } + break; + case SC_Manufacturer: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS Manufacturer = "); + for (i = 0; i < wps_len; i++) + printf("%c", *array_ptr++); + printf("\n"); + } + break; + case SC_Model_Name: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS Model Name = "); + for (i = 0; i < wps_len; i++) + printf("%c", *array_ptr++); + printf("\n"); + } + break; + case SC_Model_Number: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS Model Number = "); + for (i = 0; i < wps_len; i++) + printf("%c", *array_ptr++); + printf("\n"); + } + break; + case SC_Serial_Number: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS Serial Number = "); + for (i = 0; i < wps_len; i++) + printf("%c", *array_ptr++); + printf("\n"); + } + break; + case SC_Vendor_Extension: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + array_ptr = wps_tlv->data; + printf("\t WPS 2.0 Vendor Extension = "); + for (i = 0; i < wps_len; i++) + printf("%x", *array_ptr++); + printf("\n"); + } + break; + case SC_Selected_Registrar: + { + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + printf("\t Selected Registrar = %d\n", + *(wps_tlv->data)); + } + break; + case SC_SelectedRegistrarConfigMethods: + { + t_u16 sr_config_methods = 0; + tlvbuf_wps_ie *wps_tlv = (tlvbuf_wps_ie *)ptr; + memcpy(&sr_config_methods, wps_tlv->data, + sizeof(t_u16)); + sr_config_methods = ntohs(sr_config_methods); + printf("\t Selected Registrar Configuration Methods = 0x%x\n", sr_config_methods); + } + break; + default: + printf("unknown ie=0x%x, len=%d\n", wps_type, wps_len); + break; + } + ptr += wps_len + sizeof(tlvbuf_wps_ie); + /* Take care of error condition */ + if (wps_len + sizeof(tlvbuf_wps_ie) <= ie_len_wps) + ie_len_wps -= wps_len + sizeof(tlvbuf_wps_ie); + else + ie_len_wps = 0; + } +} + +/** + * @brief Print WIFIDIRECT IE elements from event payload + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_wifidirect_ie_elements(t_u8 *buffer, t_u16 size) +{ + t_u8 *ptr = buffer; + t_u8 *array_ptr, *orig_ptr = NULL; + int i; + static t_u16 len = 0; + static t_u16 saved_len = 0; + static t_u16 pending_len = 0; + static t_u8 type = 0; + static t_u8 next_byte = WIFIDIRECT_OVERLAP_TYPE; + static t_u8 saved_data[WIFI_IE_MAX_PAYLOAD] = { 0 }; + t_u16 temp; + t_u16 left_len = size; + + while (left_len > 0) { + if (next_byte == WIFIDIRECT_OVERLAP_TYPE) { + type = *ptr; + next_byte = WIFIDIRECT_OVERLAP_LEN; + left_len--; + ptr++; + } + if (left_len >= sizeof(len) && + next_byte == WIFIDIRECT_OVERLAP_LEN) { + memcpy(&len, ptr, sizeof(t_u16)); + len = uap_le16_to_cpu(len); + next_byte = WIFIDIRECT_OVERLAP_DATA; + left_len -= sizeof(t_u16); + ptr += sizeof(t_u16); + + /* case when Type, Len in one frame and data in next */ + if (left_len == 0) { + memcpy(saved_data, + ptr - WIFIDIRECT_IE_HEADER_LEN, + WIFIDIRECT_IE_HEADER_LEN); + saved_len = WIFIDIRECT_IE_HEADER_LEN; + pending_len = len; + } + } + if (left_len > 0 && next_byte == WIFIDIRECT_OVERLAP_DATA) { + /* copy next data */ + if (pending_len > 0 && + (left_len <= (WIFI_IE_MAX_PAYLOAD - saved_len))) { + memcpy(saved_data + saved_len, ptr, + pending_len); + orig_ptr = ptr; + ptr = saved_data; + } else { + ptr -= WIFIDIRECT_IE_HEADER_LEN; + } + + if (!pending_len && !orig_ptr && left_len < len) { + /* save along with type and len */ + memcpy(saved_data, + ptr - WIFIDIRECT_IE_HEADER_LEN, + left_len + WIFIDIRECT_IE_HEADER_LEN); + saved_len = left_len + WIFIDIRECT_IE_HEADER_LEN; + pending_len = len - left_len; + break; + } + switch (type) { + case TLV_TYPE_WIFIDIRECT_DEVICE_ID: + { + tlvbuf_wifidirect_device_id + *wifidirect_tlv = + (tlvbuf_wifidirect_device_id *) + ptr; + printf("\t Device ID - "); + print_mac(wifidirect_tlv-> + dev_mac_address); + printf("\n"); + } + break; + case TLV_TYPE_WIFIDIRECT_CAPABILITY: + { + tlvbuf_wifidirect_capability + *wifidirect_tlv = + (tlvbuf_wifidirect_capability *) + ptr; + printf("\t Device capability = %d\n", + (int)wifidirect_tlv-> + dev_capability); + printf("\t Group capability = %d\n", + (int)wifidirect_tlv-> + group_capability); + } + break; + case TLV_TYPE_WIFIDIRECT_GROUPOWNER_INTENT: + { + tlvbuf_wifidirect_group_owner_intent + *wifidirect_tlv = + (tlvbuf_wifidirect_group_owner_intent + *)ptr; + printf("\t Group owner intent = %d\n", + (int)wifidirect_tlv->dev_intent); + } + break; + case TLV_TYPE_WIFIDIRECT_MANAGEABILITY: + { + tlvbuf_wifidirect_manageability + *wifidirect_tlv = + (tlvbuf_wifidirect_manageability + *)ptr; + printf("\t Manageability = %d\n", + (int)wifidirect_tlv-> + manageability); + } + break; + case TLV_TYPE_WIFIDIRECT_INVITATION_FLAG: + { + tlvbuf_wifidirect_invitation_flag + *wifidirect_tlv = + (tlvbuf_wifidirect_invitation_flag + *)ptr; + printf("\t Invitation Flag = %d\n", + (int)wifidirect_tlv-> + invitation_flag & + INVITATION_FLAG_MASK); + } + break; + case TLV_TYPE_WIFIDIRECT_CHANNEL_LIST: + { + tlvbuf_wifidirect_channel_list + *wifidirect_tlv = + (tlvbuf_wifidirect_channel_list + *)ptr; + chan_entry *temp_ptr; + printf("\t Country String %c%c", + wifidirect_tlv-> + country_string[0], + wifidirect_tlv-> + country_string[1]); + if (isalpha + (wifidirect_tlv->country_string[2])) + printf("%c", + wifidirect_tlv-> + country_string[2]); + printf("\n"); + temp_ptr = + (chan_entry *)wifidirect_tlv-> + wifidirect_chan_entry_list; + temp = uap_le16_to_cpu(wifidirect_tlv-> + length) - + (sizeof + (tlvbuf_wifidirect_channel_list) + - WIFIDIRECT_IE_HEADER_LEN); + while (temp) { + printf("\t Regulatory_class = %d\n", (int)(temp_ptr->regulatory_class)); + printf("\t No of channels = %d\n", (int)temp_ptr->num_of_channels); + printf("\t Channel list = "); + for (i = 0; + i < + temp_ptr->num_of_channels; + i++) { + printf("%d ", + *(temp_ptr-> + chan_list + + i)); + } + printf("\n"); + temp -= sizeof(chan_entry) + + temp_ptr-> + num_of_channels; + temp_ptr = + (chan_entry *)((t_u8 *) + temp_ptr + + + sizeof + (chan_entry) + + + temp_ptr-> + num_of_channels); + } + printf("\n"); + } + break; + case TLV_TYPE_WIFIDIRECT_NOTICE_OF_ABSENCE: + { + tlvbuf_wifidirect_notice_of_absence + *wifidirect_tlv = + (tlvbuf_wifidirect_notice_of_absence + *)ptr; + noa_descriptor *temp_ptr; + printf("\t Instance of Notice of absence timing %d\n", (int)wifidirect_tlv->noa_index); + printf("\t CTWindow and Opportunistic power save parameters %d\n", (int)wifidirect_tlv->ctwindow_opp_ps); + temp_ptr = + (noa_descriptor *) + wifidirect_tlv-> + wifidirect_noa_descriptor_list; + temp = uap_le16_to_cpu(wifidirect_tlv-> + length) - + (sizeof + (tlvbuf_wifidirect_notice_of_absence) + - WIFIDIRECT_IE_HEADER_LEN); + while (temp) { + printf("\t Count or Type = %d\n", (int)temp_ptr->count_type); + printf("\t Duration = %dms\n", + uap_le32_to_cpu + (temp_ptr->duration)); + printf("\t Interval = %dms\n", + uap_le32_to_cpu + (temp_ptr->interval)); + printf("\t Start Time = %d\n", + uap_le32_to_cpu + (temp_ptr->start_time)); + printf("\n"); + temp_ptr += + sizeof(noa_descriptor); + temp -= sizeof(noa_descriptor); + } + } + break; + case TLV_TYPE_WIFIDIRECT_DEVICE_INFO: + { + tlvbuf_wifidirect_device_info + *wifidirect_tlv = + (tlvbuf_wifidirect_device_info + *)ptr; + printf("\t Device address - "); + print_mac(wifidirect_tlv->dev_address); + printf("\n"); + printf("\t Config methods - 0x%02X\n", + ntohs(wifidirect_tlv-> + config_methods)); + printf("\t Primay device type = %02d-%02X%02X%02X%02X-%02d\n", (int)ntohs(wifidirect_tlv->primary_category), (int)wifidirect_tlv->primary_oui[0], (int)wifidirect_tlv->primary_oui[1], (int)wifidirect_tlv->primary_oui[2], (int)wifidirect_tlv->primary_oui[3], (int)ntohs(wifidirect_tlv->primary_subcategory)); + printf("\t Secondary Device Count = %d\n", (int)wifidirect_tlv->secondary_dev_count); + array_ptr = + wifidirect_tlv-> + secondary_dev_info; + for (i = 0; + i < + wifidirect_tlv-> + secondary_dev_count; i++) { + memcpy(&temp, array_ptr, + sizeof(t_u16)); + printf("\t Secondary device type = %02d-", ntohs(temp)); + array_ptr += sizeof(temp); + printf("%02X%02X%02X%02X", + array_ptr[0], + array_ptr[1], + array_ptr[2], + array_ptr[3]); + array_ptr += 4; + memcpy(&temp, array_ptr, + sizeof(t_u16)); + printf("-%02d\n", ntohs(temp)); + array_ptr += sizeof(temp); + } + /* display device name */ + array_ptr = + wifidirect_tlv->device_name + + wifidirect_tlv-> + secondary_dev_count * + WPS_DEVICE_TYPE_LEN; + if (*(t_u16 *) + (((t_u8 *)(&wifidirect_tlv-> + device_name_len)) + + wifidirect_tlv-> + secondary_dev_count * + WPS_DEVICE_TYPE_LEN)) + printf("\t Device Name = "); + memcpy(&temp, + (((t_u8 *)(&wifidirect_tlv-> + device_name_len)) + + wifidirect_tlv-> + secondary_dev_count * + WPS_DEVICE_TYPE_LEN), + sizeof(t_u16)); + temp = ntohs(temp); + for (i = 0; i < temp; i++) + printf("%c", *array_ptr++); + printf("\n"); + } + break; + case TLV_TYPE_WIFIDIRECT_GROUP_INFO: + { + tlvbuf_wifidirect_group_info + *wifidirect_tlv = + (tlvbuf_wifidirect_group_info *) + ptr; + t_u8 wifidirect_client_dev_length; + wifidirect_client_dev_info *temp_ptr; + temp_ptr = + (wifidirect_client_dev_info *) + wifidirect_tlv-> + wifidirect_client_dev_list; + if (temp_ptr == NULL) + break; + wifidirect_client_dev_length = + temp_ptr->dev_length; + temp = uap_le16_to_cpu(wifidirect_tlv-> + length) - + wifidirect_client_dev_length; + while (temp) { + + printf("\t Group WifiDirect Client Device address - "); + print_mac(temp_ptr-> + wifidirect_dev_address); + printf("\n"); + printf("\t Group WifiDirect Client Interface address - "); + print_mac(temp_ptr-> + wifidirect_intf_address); + printf("\n"); + printf("\t Group WifiDirect Client Device capability = %d\n", (int)temp_ptr->wifidirect_dev_capability); + printf("\t Group WifiDirect Client Config methods - 0x%02X\n", ntohs(temp_ptr->config_methods)); + printf("\t Group WifiDirect Client Primay device type = %02d-%02X%02X%02X%02X-%02d\n", (int)ntohs(temp_ptr->primary_category), (int)temp_ptr->primary_oui[0], (int)temp_ptr->primary_oui[1], (int)temp_ptr->primary_oui[2], (int)temp_ptr->primary_oui[3], (int)ntohs(temp_ptr->primary_subcategory)); + printf("\t Group WifiDirect Client Secondary Device Count = %d\n", (int)temp_ptr->wifidirect_secondary_dev_count); + array_ptr = + temp_ptr-> + wifidirect_secondary_dev_info; + for (i = 0; + i < + temp_ptr-> + wifidirect_secondary_dev_count; + i++) { + memcpy(&temp, array_ptr, + sizeof(t_u16)); + printf("\t Group WifiDirect Client Secondary device type = %02d-", ntohs(temp)); + array_ptr += + sizeof(temp); + printf("%02X%02X%02X%02X", array_ptr[0], array_ptr[1], array_ptr[2], array_ptr[3]); + array_ptr += 4; + memcpy(&temp, array_ptr, + sizeof(t_u16)); + printf("-%02d\n", + ntohs(temp)); + array_ptr += + sizeof(temp); + } + /* display device name */ + array_ptr = + temp_ptr-> + wifidirect_device_name + + temp_ptr-> + wifidirect_secondary_dev_count + * WPS_DEVICE_TYPE_LEN; + printf("\t Group WifiDirect Device Name = "); + memcpy(&temp, + (((t_u8 *)(&temp_ptr-> + wifidirect_device_name_len)) + + + temp_ptr-> + wifidirect_secondary_dev_count + * WPS_DEVICE_TYPE_LEN), + sizeof(t_u16)); + temp = ntohs(temp); + for (i = 0; i < temp; i++) + printf("%c", + *array_ptr++); + printf("\n"); + temp_ptr += + wifidirect_client_dev_length; + temp -= wifidirect_client_dev_length; + if (temp_ptr) + wifidirect_client_dev_length + = + temp_ptr-> + dev_length; + } + printf("\n"); + } + break; + case TLV_TYPE_WIFIDIRECT_GROUP_ID: + { + tlvbuf_wifidirect_group_id + *wifidirect_tlv = + (tlvbuf_wifidirect_group_id *) + ptr; + printf("\t Group address - "); + print_mac(wifidirect_tlv-> + group_address); + printf("\n"); + printf("\t Group ssid = "); + for (i = 0; + (unsigned int)i < + uap_le16_to_cpu(wifidirect_tlv-> + length) + - + (sizeof(tlvbuf_wifidirect_group_id) + - WIFIDIRECT_IE_HEADER_LEN); i++) + printf("%c", + wifidirect_tlv-> + group_ssid[i]); + printf("\n"); + } + break; + case TLV_TYPE_WIFIDIRECT_GROUP_BSS_ID: + { + tlvbuf_wifidirect_group_bss_id + *wifidirect_tlv = + (tlvbuf_wifidirect_group_bss_id + *)ptr; + printf("\t Group BSS Id - "); + print_mac(wifidirect_tlv->group_bssid); + printf("\n"); + } + break; + case TLV_TYPE_WIFIDIRECT_INTERFACE: + { + tlvbuf_wifidirect_interface + *wifidirect_tlv = + (tlvbuf_wifidirect_interface *) + ptr; + printf("\t Interface Id - "); + print_mac(wifidirect_tlv->interface_id); + printf("\t Interface count = %d", + (int)wifidirect_tlv-> + interface_count); + for (i = 0; + i < + wifidirect_tlv->interface_count; + i++) { + printf("\n\t Interface address [%d]", i + 1); + print_mac(&wifidirect_tlv-> + interface_idlist[i * + ETH_ALEN]); + } + printf("\n"); + } + break; + case TLV_TYPE_WIFIDIRECT_CHANNEL: + { + tlvbuf_wifidirect_channel + *wifidirect_tlv = + (tlvbuf_wifidirect_channel *) + ptr; + printf("\t Listen Channel Country String %c%c", wifidirect_tlv->country_string[0], wifidirect_tlv->country_string[1]); + if (isalpha + (wifidirect_tlv->country_string[2])) + printf("%c", + wifidirect_tlv-> + country_string[2]); + printf("\n"); + printf("\t Listen Channel regulatory class = %d\n", (int)wifidirect_tlv->regulatory_class); + printf("\t Listen Channel number = %d\n", (int)wifidirect_tlv->channel_number); + } + break; + + case TLV_TYPE_WIFIDIRECT_OPCHANNEL: + { + tlvbuf_wifidirect_channel + *wifidirect_tlv = + (tlvbuf_wifidirect_channel *) + ptr; + printf("\t Operating Channel Country String %c%c", wifidirect_tlv->country_string[0], wifidirect_tlv->country_string[1]); + if (isalpha + (wifidirect_tlv->country_string[2])) + printf("%c", + wifidirect_tlv-> + country_string[2]); + printf("\n"); + printf("\t Operating Channel regulatory class = %d\n", (int)wifidirect_tlv->regulatory_class); + printf("\t Operating Channel number = %d\n", (int)wifidirect_tlv->channel_number); + } + break; + + case TLV_TYPE_WIFIDIRECT_CONFIG_TIMEOUT: + { + tlvbuf_wifidirect_config_timeout + *wifidirect_tlv = + (tlvbuf_wifidirect_config_timeout + *)ptr; + printf("\t GO configuration timeout = %d msec\n", (int)wifidirect_tlv->group_config_timeout * 10); + printf("\t Client configuration timeout = %d msec\n", (int)wifidirect_tlv->device_config_timeout * 10); + } + break; + case TLV_TYPE_WIFIDIRECT_EXTENDED_LISTEN_TIME: + { + tlvbuf_wifidirect_ext_listen_time + *wifidirect_tlv = + (tlvbuf_wifidirect_ext_listen_time + *)ptr; + printf("\t Availability Period = %d msec\n", (int)wifidirect_tlv->availability_period); + printf("\t Availability Interval = %d msec\n", (int)wifidirect_tlv->availability_interval); + } + break; + case TLV_TYPE_WIFIDIRECT_INTENDED_ADDRESS: + { + tlvbuf_wifidirect_intended_addr + *wifidirect_tlv = + (tlvbuf_wifidirect_intended_addr + *)ptr; + printf("\t Intended Interface Address - "); + print_mac(wifidirect_tlv-> + group_address); + printf("\n"); + } + break; + + case TLV_TYPE_WIFIDIRECT_STATUS: + { + tlvbuf_wifidirect_status *wifidirect_tlv + = + (tlvbuf_wifidirect_status *)ptr; + printf("\t Status = %d\n", + wifidirect_tlv->status_code); + } + break; + + default: + printf("unknown ie=0x%x, len=%d\n", type, len); + break; + } + next_byte = WIFIDIRECT_OVERLAP_TYPE; + if (orig_ptr) + ptr = orig_ptr + pending_len; + } + ptr += len + WIFIDIRECT_IE_HEADER_LEN; + left_len -= len; + } + printf("\n"); + return; +} + +/** + * @brief Parse and print WIFIDIRECT generic event data + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_wifidirect_generic(t_u8 *buffer, t_u16 size) +{ + const t_u8 wifi_oui[3] = { 0x50, 0x6F, 0x9A }; + const t_u8 wps_oui[3] = { 0x00, 0x50, 0xF2 }; + apeventbuf_wifidirect_generic *wifidirect_event; + wifidirect_ie_header *wifidirect_wps_header; + t_u16 wifidirect_wps_len = 0, type, sub_type; + printf("EVENT: WIFIDIRECT \n"); + wifidirect_event = (apeventbuf_wifidirect_generic *)(buffer); + printf("Event length = %d\n", + uap_le16_to_cpu(wifidirect_event->event_length)); + printf("Event Type = "); + type = uap_le16_to_cpu(wifidirect_event->event_type); + switch (type) { + case 0: + printf("Negotiation Request\n"); + break; + case 1: + printf("Negotiation Response\n"); + break; + case 2: + printf("Negotiation Result\n"); + break; + case 3: + printf("Invitation Request\n"); + break; + case 4: + printf("Invitation Response\n"); + break; + case 5: + printf("Discoverability Request\n"); + break; + case 6: + printf("Discoverability Response\n"); + break; + case 7: + printf("Provision Discovery Request\n"); + break; + case 8: + printf("Provision Discovery Response\n"); + break; + case 9: + printf("GO Negotiation response Tx Event\n"); + break; + case 10: + printf("GO Negotiation confirm Tx Event\n"); + break; + case 14: + printf("Peer Detected event\n"); + break; + case 15: + printf("Client associated event\n"); + break; + case 16: + printf("FW debug event: %s\n", + wifidirect_event->entire_ie_list); + return; + default: + printf("Unknown\n"); + break; + } + sub_type = uap_le16_to_cpu(wifidirect_event->event_sub_type); + if (type == 2) { + switch (sub_type) { + case 0: + printf("Event SubType = No Role\n"); + break; + case 1: + printf("Event SubType = Group Owner Role\n"); + break; + case 2: + printf("Event SubType = Client Role\n"); + break; + default: + printf("Event SubType = %d\n", sub_type); + break; + } + } else if (type == 3 || type == 4) { + switch (sub_type & 0x03) { /* lower 2 bits */ + case 0: + printf("Event SubType = No Role\n"); + break; + case 1: + printf("Event SubType = Group Owner Role\n"); + break; + case 2: + printf("Event SubType = Client Role\n"); + break; + } + switch ((sub_type & 0x1C) >> 2) { /* next 3 bits */ + case 0: + printf("Packet processing state = None\n"); + break; + case 1: + printf("Packet processing state = Processing\n"); + break; + case 2: + printf("Packet processing state = Insufficient information, Dropped\n"); + break; + case 3: + printf("Packet processing state = Success\n"); + break; + case 4: + printf("Packet processing state = Fail\n"); + break; + default: + printf("Event SubType = %d\n", sub_type); + break; + } + } else if (type == 7 || type == 8) { + switch (sub_type) { + case 0: + printf("Event SubType = No Config Method\n"); + break; + case 8: + printf("Event SubType = Config Method Display\n"); + break; + case 0x80: + printf("Event SubType = Config Method Push Button\n"); + break; + case 0x100: + printf("Event SubType = Config Method Keypad\n"); + break; + default: + printf("Event SubType = %d\n", sub_type); + break; + } + } + printf("Peer Mac Address - "); + print_mac(wifidirect_event->peer_mac_addr); + printf("\n"); + /* Print rest of IE elements */ + wifidirect_wps_header = + (wifidirect_ie_header *)(wifidirect_event->entire_ie_list); + wifidirect_wps_len = uap_le16_to_cpu(wifidirect_event->event_length) + - sizeof(apeventbuf_wifidirect_generic); + + while (wifidirect_wps_len >= sizeof(wifidirect_ie_header)) { + if (!memcmp + (wifidirect_wps_header->oui, wifi_oui, sizeof(wifi_oui)) || + !(memcmp + (wifidirect_wps_header->oui, wps_oui, sizeof(wps_oui)))) { + switch (wifidirect_wps_header->oui_type) { + case WIFIDIRECT_OUI_TYPE: + print_wifidirect_ie_elements + (wifidirect_wps_header->ie_list, + wifidirect_wps_header->ie_length - + sizeof(wifidirect_wps_header->oui) + - + sizeof(wifidirect_wps_header-> + oui_type)); + printf("\n"); + break; + case WIFI_WPS_OUI_TYPE: + print_wifi_wps_ie_elements + (wifidirect_wps_header->ie_list, + wifidirect_wps_header->ie_length - + sizeof(wifidirect_wps_header->oui) + - + sizeof(wifidirect_wps_header-> + oui_type)); + printf("\n"); + break; + } + } + wifidirect_wps_len -= + wifidirect_wps_header->ie_length + IE_HEADER_LEN; + wifidirect_wps_header = + (wifidirect_ie_header *)(((t_u8 *)wifidirect_wps_header) + + + wifidirect_wps_header-> + ie_length + IE_HEADER_LEN); + } +} + +/** + * @brief Parse and print WIFIDIRECT service discovery event data + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_wifidirect_service_discovery(t_u8 *buffer, t_u16 size) +{ + unsigned int i; + t_u16 event_len = 0; + t_u16 dns_len = 0, dns_type; + t_u8 action = 0; /* req = 0, resp = 1 */ + apeventbuf_wifidirect_discovery_request *wifidirect_disc_req; + apeventbuf_wifidirect_discovery_response *wifidirect_disc_resp; + printf("EVENT: WIFIDIRECT SERVICE DISCOVERY\n"); + memcpy(&event_len, buffer, sizeof(t_u16)); + event_len = uap_le16_to_cpu(event_len); + printf("Event length = %d\n", event_len); + printf("Service Discovery packet:\n"); + /* check request /response Byte at offset 2+6+2 */ + action = *(buffer + sizeof(t_u16) + ETH_ALEN + sizeof(t_u8)); + if (action == WIFIDIRECT_DISCOVERY_REQUEST_ACTION) { + wifidirect_disc_req = + (apeventbuf_wifidirect_discovery_request *)(buffer + + sizeof + (t_u16)); + printf("\t Peer Mac Address - "); + print_mac(wifidirect_disc_req->peer_mac_addr); + printf("\n\t Category = %d\n", wifidirect_disc_req->category); + printf("\t Action = %d\n", wifidirect_disc_req->action); + printf("\t Dialog taken = %d\n", + wifidirect_disc_req->dialog_taken); + printf("\t Advertize protocol IE - 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", wifidirect_disc_req->advertize_protocol_ie[0], wifidirect_disc_req->advertize_protocol_ie[1], wifidirect_disc_req->advertize_protocol_ie[2], wifidirect_disc_req->advertize_protocol_ie[3]); + printf("\t Request query length = %d\n", + uap_le16_to_cpu(wifidirect_disc_req->query_len)); + printf("\t Information Id - 0x%02x, 0x%02x\n", + wifidirect_disc_req->info_id[0], + wifidirect_disc_req->info_id[1]); + printf("\t Request length = %d\n", + uap_le16_to_cpu(wifidirect_disc_req->request_len)); + printf("\t OUI - 0x%02x, 0x%02x, 0x%02x\n", + wifidirect_disc_req->oui[0], wifidirect_disc_req->oui[1], + wifidirect_disc_req->oui[2]); + printf("\t OUI sub type = %d\n", + wifidirect_disc_req->oui_sub_type); + printf("\t Service update Indicator = %d\n", + uap_le16_to_cpu(wifidirect_disc_req-> + service_update_indicator)); + printf("\t Vendor length = %d\n", + uap_le16_to_cpu(wifidirect_disc_req->vendor_len)); + printf("\t Service protocol = %d\n", + wifidirect_disc_req->service_protocol); + printf("\t Service transaction Id = %d\n", + wifidirect_disc_req->service_transaction_id); + printf("\t Query Data = "); + if (wifidirect_disc_req->service_protocol == 1) { + printf(" * Bonjour * \n"); + printf("\t\t DNS = "); + dns_len = + uap_le16_to_cpu(wifidirect_disc_req-> + vendor_len) - + WIFIDIRECT_DISCOVERY_BONJOUR_FIXED_LEN; + for (i = 0; i < dns_len; i++) + printf("%02x ", + (int)*(wifidirect_disc_req->disc_query.u. + bonjour.dns + i)); + memcpy(&dns_type, + (&wifidirect_disc_req->disc_query.u.bonjour. + dns_type + dns_len), sizeof(dns_type)); + dns_type = uap_le16_to_cpu(dns_type); + printf("\n\t\t DNS Type = %d\n", dns_type); + printf("\t\t Version = %d\n", + *(&wifidirect_disc_req->disc_query.u.bonjour. + version + dns_len)); + } else if (wifidirect_disc_req->service_protocol == 2) { + printf(" * uPnP * \n"); + printf("\t\t Version = %d\n", + wifidirect_disc_req->disc_query.u.upnp.version); + dns_len = + uap_le16_to_cpu(wifidirect_disc_req-> + vendor_len) - + WIFIDIRECT_DISCOVERY_UPNP_FIXED_LEN; + printf("\t\t Value = "); + for (i = 0; i < dns_len; i++) + printf("%02x ", + (int)*(wifidirect_disc_req->disc_query.u. + upnp.value + i)); + } + printf("\n"); + } else if (action == WIFIDIRECT_DISCOVERY_RESPONSE_ACTION) { + wifidirect_disc_resp = + (apeventbuf_wifidirect_discovery_response *)(buffer + + sizeof + (t_u16)); + printf("\t Peer Mac Address - "); + print_mac(wifidirect_disc_resp->peer_mac_addr); + printf("\n\t Category = %d\n", wifidirect_disc_resp->category); + printf("\t Action = %d\n", wifidirect_disc_resp->action); + printf("\t Dialog taken = %d\n", + wifidirect_disc_resp->dialog_taken); + printf("\t Status code = %d\n", + wifidirect_disc_resp->status_code); + printf("\t GAS reply - 0x%02x\n", + uap_le16_to_cpu(wifidirect_disc_resp->gas_reply)); + printf("\t Advertize protocol IE - 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", wifidirect_disc_resp->advertize_protocol_ie[0], wifidirect_disc_resp->advertize_protocol_ie[1], wifidirect_disc_resp->advertize_protocol_ie[2], wifidirect_disc_resp->advertize_protocol_ie[3]); + printf("\t Response query length = %d\n", + uap_le16_to_cpu(wifidirect_disc_resp->query_len)); + printf("\t Information Id - 0x%02x, 0x%02x\n", + wifidirect_disc_resp->info_id[0], + wifidirect_disc_resp->info_id[1]); + printf("\t Response length = %d\n", + uap_le16_to_cpu(wifidirect_disc_resp->response_len)); + printf("\t OUI - 0x%02x, 0x%02x, 0x%02x\n", + wifidirect_disc_resp->oui[0], + wifidirect_disc_resp->oui[1], + wifidirect_disc_resp->oui[2]); + printf("\t OUI sub type = %d\n", + wifidirect_disc_resp->oui_sub_type); + printf("\t Service update Indicator = %d\n", + uap_le16_to_cpu(wifidirect_disc_resp-> + service_update_indicator)); + printf("\t Vendor length = %d\n", + uap_le16_to_cpu(wifidirect_disc_resp->vendor_len)); + printf("\t Service protocol = %d\n", + wifidirect_disc_resp->service_protocol); + printf("\t Service transaction Id = %d\n", + wifidirect_disc_resp->service_transaction_id); + printf("\t Status Code = %d\n", + wifidirect_disc_resp->disc_status_code); + printf("\t Response Data = "); + if (wifidirect_disc_resp->service_protocol == 1) { + printf(" * Bonjour * \n"); + /* + printf("\t\t DNS = "); + dns_len = uap_le16_to_cpu(wifidirect_disc_resp->vendor_len) - + (WIFIDIRECT_DISCOVERY_BONJOUR_FIXED_LEN + 1); + for( i=0; i < dns_len; i++) + printf("%c",*(wifidirect_disc_resp->disc_resp.u.bonjour.dns + i)); + memcpy(&dns_type, (&wifidirect_disc_req->disc_query.u.bonjour.dns_type + + dns_len), sizeof(dns_type)); + dns_type = uap_le16_to_cpu(dns_type); + printf("\n\t\t DNS Type = %d\n", dns_type); + printf("\t\t Version = %d\n", *(&wifidirect_disc_resp->disc_resp.u.bonjour.version + + dns_len)); + */ + } else if (wifidirect_disc_resp->service_protocol == 2) { + printf(" * uPnP * \n"); + printf("\t\t Version = %d\n", + wifidirect_disc_resp->disc_resp.u.upnp.version); + dns_len = + uap_le16_to_cpu(wifidirect_disc_resp-> + vendor_len) - + WIFIDIRECT_DISCOVERY_UPNP_FIXED_LEN; + printf("\t\t Value = "); + for (i = 0; i < dns_len; i++) + printf("%02x ", + (int)*(wifidirect_disc_resp->disc_resp.u. + upnp.value + i)); + } + printf("\n"); + } +} +#endif + +/** + * @brief Parse and print TDLS generic event data + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_tdls_generic(t_u8 *buffer, t_u16 size) +{ + eventbuf_tdls_generic *tdls_event; + eventbuf_tdls_debug *tdls_debug; + eventbuf_tdls_packet *tdls_pkt; + printf("EVENT: TDLS\n"); + tdls_event = (eventbuf_tdls_generic *)(buffer); + printf("Event Type = "); + switch (uap_le16_to_cpu(tdls_event->event_type)) { + case TDLS_EVENT_TYPE_SETUP_FAILURE: + printf("TDLS setup failure\n"); + break; + case TDLS_EVENT_TYPE_SETUP_REQ_RCVD: + printf("TDLS setup request received from peer\n"); + break; + case TDLS_EVENT_TYPE_LINK_TORN_DOWN: + printf("TDLS link torn down\n"); + break; + case TDLS_EVENT_TYPE_LINK_ESTABLISHED: + printf("TDLS link established\n"); + break; + case TDLS_EVENT_TYPE_DEBUG: + tdls_debug = (eventbuf_tdls_debug *)(buffer); + printf("TDLS debug event, "); + printf("TSF = %lld\n", uap_le64_to_cpu(tdls_debug->tsf)); + printf("String = "); + printf((char *)(tdls_debug->string), + uap_le32_to_cpu(tdls_debug->first_arg), + uap_le32_to_cpu(tdls_debug->second_arg), + uap_le32_to_cpu(tdls_debug->third_arg)); + printf("\n"); + return; + case TDLS_EVENT_TYPE_PACKET: + tdls_pkt = (eventbuf_tdls_packet *)(buffer); + printf("TDLS packet len=%d\n", tdls_pkt->length); + hexdump((t_u8 *)tdls_pkt->data, tdls_pkt->length, ' '); + return; + case TDLS_EVENT_TYPE_CHAN_SWITCH_RESULT: + if (tdls_event->u.switch_result.status) { + printf("TDLS Channel Switch Failed! current channel=%d Reasoncode %d\n", tdls_event->u.switch_result.current_channel, tdls_event->u.switch_result.reason); + } else { + printf("TDLS Channel Switch Successful. current channel=%d\n", tdls_event->u.switch_result.current_channel); + } + break; + case TDLS_EVENT_TYPE_START_CHAN_SWITCH: + printf("TDLS Start channel switch...\n"); + break; + case TDLS_EVENT_TYPE_CHAN_SWITCH_STOPPED: + printf("TDLS Channel Switch stopped! reason=%d\n", + tdls_event->u.cs_stop_reason); + break; + default: + printf("Unknown\n"); + break; + } + printf("Peer Mac Address - "); + print_mac(tdls_event->peer_mac_addr); + printf("\n"); + if (uap_le16_to_cpu(tdls_event->event_type) == + TDLS_EVENT_TYPE_SETUP_FAILURE) { + switch (uap_le16_to_cpu(tdls_event->u.reason_code)) { + case 1: + printf("Reason Code = Internal error\n"); + break; + case 7: + printf("Reason Code = Timeout waiting for response\n"); + break; + case 8: + printf("Reason Code = Non-zero setup response status\n"); + break; + default: + printf("Reason Code = %d\n", + uap_le16_to_cpu(tdls_event->u.reason_code)); + break; + } + } else if (uap_le16_to_cpu(tdls_event->event_type) == + TDLS_EVENT_TYPE_LINK_TORN_DOWN) { + switch (uap_le16_to_cpu(tdls_event->u.reason_code)) { + case 3: + printf("Reason Code = Peer station is leaving BSS\n"); + break; + case 25: + printf("Reason Code = Peer station is unreachable on direct link\n"); + break; + case 26: + printf("Reason Code = Unspecified\n"); + break; + case 211: + printf("Reason Code = Setup received from TDLS peer\n"); + break; + case 212: + printf("Reason Code = Clearing TDLS connections due to deauth/disassoc\n"); + break; + default: + printf("Reason Code = %d\n", + uap_le16_to_cpu(tdls_event->u.reason_code)); + break; + } + } else if (uap_le16_to_cpu(tdls_event->event_type) == + TDLS_EVENT_TYPE_LINK_ESTABLISHED) { + int ie_len = uap_le16_to_cpu(tdls_event->u.ie_length); + if (ie_len) { + printf("Peer IE len=%d\n", ie_len); + hexdump((t_u8 *)tdls_event->ie_ptr, ie_len, ' '); + } + } +} + +/** + * @brief Prints station reject state + * + * @param state Fail state + * @return N/A + */ +void +print_reject_state(t_u8 state) +{ + switch (state) { + case REJECT_STATE_FAIL_EAPOL_2: + printf("Reject state: FAIL_EAPOL_2\n"); + break; + case REJECT_STATE_FAIL_EAPOL_4: + printf("Reject state: FAIL_EAPOL_4:\n"); + break; + case REJECT_STATE_FAIL_EAPOL_GROUP_2: + printf("Reject state: FAIL_EAPOL_GROUP_2\n"); + break; + default: + printf("ERR: unknown reject state %d\n", state); + break; + } + return; +} + +/** + * @brief Prints station reject reason + * + * @param reason Reason code + * @return N/A + */ +void +print_reject_reason(t_u16 reason) +{ + switch (reason) { + case IEEEtypes_REASON_INVALID_IE: + printf("Reject reason: Invalid IE\n"); + break; + case IEEEtypes_REASON_MIC_FAILURE: + printf("Reject reason: Mic Failure\n"); + break; + default: + printf("Reject reason: %d\n", reason); + break; + } + return; +} + +/** + * @brief Prints EAPOL state + * + * @param state Eapol state + * @return N/A + */ +void +print_eapol_state(t_u8 state) +{ + switch (state) { + case EAPOL_START: + printf("Eapol state: EAPOL_START\n"); + break; + case EAPOL_WAIT_PWK2: + printf("Eapol state: EAPOL_WAIT_PWK2\n"); + break; + case EAPOL_WAIT_PWK4: + printf("Eapol state: EAPOL_WAIT_PWK4\n"); + break; + case EAPOL_WAIT_GTK2: + printf("Eapol state: EAPOL_WAIT_GTK2\n"); + break; + case EAPOL_END: + printf("Eapol state: EAPOL_END\n"); + break; + default: + printf("ERR: unknow eapol state%d\n", state); + break; + } + return; +} + +/** + * @brief Parse and print debug event data + * + * @param buffer Pointer to received buffer + * @param size Length of the received event data + * @return N/A + */ +void +print_event_debug(t_u8 *buffer, t_u16 size) +{ + eventbuf_debug *event_body = NULL; + if (size < sizeof(eventbuf_debug)) { + printf("ERR:Event buffer too small!\n"); + return; + } + event_body = (eventbuf_debug *)buffer; + printf("Debug Event Type: %s\n", + (event_body->debug_type == 0) ? "EVENT" : "INFO"); + printf("%s log:\n", + (uap_le32_to_cpu(event_body->debug_id_major) == + DEBUG_ID_MAJ_AUTHENTICATOR) ? "Authenticator" : "Assoc_agent"); + if (uap_le32_to_cpu(event_body->debug_id_major) == + DEBUG_ID_MAJ_AUTHENTICATOR) { + switch (uap_le32_to_cpu(event_body->debug_id_minor)) { + case DEBUG_MAJ_AUTH_MIN_PWK1: + printf("EAPOL Key message 1 (PWK):\n"); + hexdump((t_u8 *)&event_body->info.eapol_pwkmsg, + sizeof(eapol_keymsg_debug_t), ' '); + break; + case DEBUG_MAJ_AUTH_MIN_PWK2: + printf("EAPOL Key message 2 (PWK):\n"); + hexdump((t_u8 *)&event_body->info.eapol_pwkmsg, + sizeof(eapol_keymsg_debug_t), ' '); + break; + case DEBUG_MAJ_AUTH_MIN_PWK3: + printf("EAPOL Key message 3 (PWK):\n"); + hexdump((t_u8 *)&event_body->info.eapol_pwkmsg, + sizeof(eapol_keymsg_debug_t), ' '); + break; + case DEBUG_MAJ_AUTH_MIN_PWK4: + printf("EAPOL Key message 4: (PWK)\n"); + hexdump((t_u8 *)&event_body->info.eapol_pwkmsg, + sizeof(eapol_keymsg_debug_t), ' '); + break; + case DEBUG_MAJ_AUTH_MIN_GWK1: + printf("EAPOL Key message 1: (GWK)\n"); + hexdump((t_u8 *)&event_body->info.eapol_pwkmsg, + sizeof(eapol_keymsg_debug_t), ' '); + break; + case DEBUG_MAJ_AUTH_MIN_GWK2: + printf("EAPOL Key message 2: (GWK)\n"); + hexdump((t_u8 *)&event_body->info.eapol_pwkmsg, + sizeof(eapol_keymsg_debug_t), ' '); + break; + case DEBUG_MAJ_AUTH_MIN_STA_REJ: + printf("Reject STA MAC: "); + print_mac(event_body->info.sta_reject.sta_mac_addr); + printf("\n"); + print_reject_state(event_body->info.sta_reject. + reject_state); + print_reject_reason(uap_le16_to_cpu + (event_body->info.sta_reject. + reject_reason)); + break; + case DEBUG_MAJ_AUTH_MIN_EAPOL_TR: + printf("STA MAC: "); + print_mac(event_body->info.eapol_state.sta_mac_addr); + printf("\n"); + print_eapol_state(event_body->info.eapol_state. + eapol_state); + break; + default: + printf("ERR: unknow debug_id_minor: %d\n", + (int)uap_le32_to_cpu(event_body-> + debug_id_minor)); + hexdump(buffer, size, ' '); + return; + } + } else if (uap_le32_to_cpu(event_body->debug_id_major) == + DEBUG_ID_MAJ_ASSOC_AGENT) { + switch (uap_le32_to_cpu(event_body->debug_id_minor)) { + case DEBUG_ID_MAJ_ASSOC_MIN_WPA_IE: + printf("STA MAC: "); + print_mac(event_body->info.wpaie.sta_mac_addr); + printf("\n"); + printf("wpa ie:\n"); + hexdump(event_body->info.wpaie.wpa_ie, MAX_WPA_IE_LEN, + ' '); + break; + case DEBUG_ID_MAJ_ASSOC_MIN_STA_REJ: + printf("Reject STA MAC: "); + print_mac(event_body->info.sta_reject.sta_mac_addr); + printf("\n"); + print_reject_state(event_body->info.sta_reject. + reject_state); + print_reject_reason(uap_le16_to_cpu + (event_body->info.sta_reject. + reject_reason)); + break; + default: + printf("ERR: unknow debug_id_minor: %d\n", + (int)uap_le32_to_cpu(event_body-> + debug_id_minor)); + hexdump(buffer, size, ' '); + return; + } + } + return; +} + +/** + * @brief Parse and print received nlist event information + * + * @param buffer Pointer to the data buffer + * @param size Length of the received event + * @return N/A + */ +void +print_event_11k_nlist_report(t_u8 *buffer, t_u16 size) +{ + int tlv_buf_left = 0; + tlvbuf_header *tlv; + nlist_entry_tlv *nlist; + int entry_num = 0; + t_u16 tlv_type, tlv_len; + + if (size < 2) { + printf("No neighbor AP list found: %d\n", size); + return; + } + tlv_buf_left = *(t_u16 *)buffer; + tlv_buf_left = uap_le16_to_cpu(tlv_buf_left); + if (tlv_buf_left < (int)sizeof(tlvbuf_header)) + return; + printf("neighbor AP list tlv len: %d\n", tlv_buf_left); + tlv = (tlvbuf_header *)(buffer + 2); + while (tlv_buf_left >= (int)sizeof(tlvbuf_header)) { + tlv_type = uap_le16_to_cpu(tlv->type); + tlv_len = uap_le16_to_cpu(tlv->len); + if ((sizeof(tlvbuf_header) + tlv_len) > + (unsigned int)tlv_buf_left) { + printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlv_len, + tlv_buf_left); + break; + } + switch (tlv_type) { + case MRVL_NEIGHBOR_REPORT_TLV_ID: + nlist = (nlist_entry_tlv *) tlv; + printf("Neighbor entry %d:\n", entry_num); + printf("BSSID: %02x:%02x:%02x:%02x:%02x:%02x \n", + nlist->bssid[0], nlist->bssid[1], + nlist->bssid[2], nlist->bssid[3], + nlist->bssid[4], nlist->bssid[5]); + printf("BSSID Info: %02x:%02x:%02x:%02x \n", + nlist->bssid_info[0], nlist->bssid_info[1], + nlist->bssid_info[2], nlist->bssid_info[3]); + printf("reg class: %d, chan: %d, phy: %d \n", + nlist->reg_class, nlist->chan, nlist->phy_type); + if (tlv_len > sizeof(nlist_entry_tlv)) { + printf("sub IE:\n"); + hexdump((t_u8 *)tlv + sizeof(nlist_entry_tlv), + tlv_len - sizeof(nlist_entry_tlv) + + sizeof(tlvbuf_header), ' '); + } + entry_num++; + break; + default: +#if DEBUG + printf("unknown tlv: %d\n", tlv_type); +#endif + break; + } + tlv_buf_left -= (sizeof(tlvbuf_header) + tlv_len); + tlv = (tlvbuf_header *)((t_u8 *)tlv + tlv_len + + sizeof(tlvbuf_header)); + } + return; +} + +/** + * @brief Parse and print received event information + * + * @param event Pointer to received event + * @param size Length of the received event + * @return N/A + */ +void +print_event(event_header *event, t_u16 size) +{ + t_u32 event_id = event->event_id; + switch (event_id) { + case MICRO_AP_EV_ID_STA_DEAUTH: + print_event_sta_deauth(event->event_data, size - EVENT_ID_LEN); + break; + case MICRO_AP_EV_ID_STA_ASSOC: + print_event_sta_assoc(event->event_data, size - EVENT_ID_LEN); + break; + case MICRO_AP_EV_ID_BSS_START: + print_event_bss_start(event->event_data, size - EVENT_ID_LEN); + break; + case MICRO_AP_EV_ID_DEBUG: + print_event_debug(event->event_data, size - EVENT_ID_LEN); + break; + case MICRO_AP_EV_BSS_IDLE: + printf("EVENT: BSS_IDLE\n"); + break; + case MICRO_AP_EV_BSS_ACTIVE: + printf("EVENT: BSS_ACTIVE\n"); + break; + case MICRO_AP_EV_RSN_CONNECT: + print_event_rsn_connect(event->event_data, size - EVENT_ID_LEN); + break; +#ifdef WIFI_DIRECT_SUPPORT + case EVENT_WIFIDIRECT_GENERIC: + print_event_wifidirect_generic(event->event_data, + size - EVENT_ID_LEN); + break; + case EVENT_WIFIDIRECT_SERVICE_DISCOVERY: + print_event_wifidirect_service_discovery(event->event_data, + size - EVENT_ID_LEN); + break; +#endif + + case EVENT_TDLS_GENERIC: + print_event_tdls_generic(event->event_data, + size - EVENT_ID_LEN); + break; + case UAP_EVENT_ID_DRV_HS_ACTIVATED: + printf("EVENT: uAP HS_ACTIVATED\n"); + break; + case UAP_EVENT_ID_DRV_HS_DEACTIVATED: + printf("EVENT: uAP HS_DEACTIVATED\n"); + break; + case UAP_EVENT_ID_HS_WAKEUP: + printf("EVENT: uAP HS_WAKEUP\n"); + break; + case UAP_EVENT_HOST_SLEEP_AWAKE: + break; + case UAP_EVENT_ID_DRV_MGMT_FRAME: + printf("EVENT: Mgmt frame from FW\n"); + hexdump((void *)event, size, ' '); + break; + case MICRO_AP_EV_WMM_STATUS_CHANGE: + printf("EVENT: WMM_STATUS_CHANGE\n"); + break; + case EVENT_RADAR_DETECTED: + printf("EVENT: RADAR_DETECTED\n"); + break; + case EVENT_CHANNEL_REPORT_RDY: + printf("EVENT: CHANNEL_REPORT_READY\n"); + hexdump((void *)event, size, ' '); + break; + case EVENT_WEP_ICV_ERROR: + print_event_wep_icv_error(event->event_data, + size - EVENT_ID_LEN); + break; + case EVENT_ID_DRV_SCAN_REPORT: + printf("Scan request completed.\n"); + break; + case EVENT_NLIST_REPORT: + printf("EVENT: 11k neighbor AP list report\n"); + print_event_11k_nlist_report(event->event_data, + size - EVENT_ID_LEN); + break; + + default: + /* Handle string based events */ +#define CUS_EVT_PORT_RELEASE "EVENT=PORT_RELEASE" + if (!strncmp + ((char *)event, CUS_EVT_PORT_RELEASE, + strlen(CUS_EVT_PORT_RELEASE))) { + printf("EVENT: PORT_RELEASE.\n"); + break; + } +#define CUS_EVT_TDLS_CONNECTED "EVENT=TDLS_CONNECTED" + if (!strncmp + ((char *)event, CUS_EVT_TDLS_CONNECTED, + strlen(CUS_EVT_TDLS_CONNECTED))) { + printf("EVENT: TDLS_CONNECTED\n"); + print_mac((t_u8 *)event + + strlen(CUS_EVT_TDLS_CONNECTED)); + printf("\n"); + break; + } +#define CUS_EVT_TDLS_TEARDOWN "EVENT=TDLS_TEARDOWN" + if (!strncmp + ((char *)event, CUS_EVT_TDLS_TEARDOWN, + strlen(CUS_EVT_TDLS_TEARDOWN))) { + printf("EVENT: TDLS_TEARDOWN\n"); + print_mac((t_u8 *)event + + strlen(CUS_EVT_TDLS_TEARDOWN)); + printf("\n"); + break; + } +#define CUS_EVT_STA_CONNECTED "EVENT=STA_CONNECTED" + if (!strncmp + ((char *)event, CUS_EVT_STA_CONNECTED, + strlen(CUS_EVT_STA_CONNECTED))) { + printf("EVENT: STA_CONNECTED\n"); + print_mac((t_u8 *)event + + strlen(CUS_EVT_STA_CONNECTED) + 1); + printf("\n"); + break; + } +#define CUS_EVT_STA_DISCONNECTED "EVENT=STA_DISCONNECTED" + if (!strncmp + ((char *)event, CUS_EVT_STA_DISCONNECTED, + strlen(CUS_EVT_STA_DISCONNECTED))) { + printf("EVENT: STA_DISCONNECTED\n"); + break; + } +#define CUS_EVT_AP_CONNECTED "EVENT=AP_CONNECTED" + if (!strncmp + ((char *)event, CUS_EVT_AP_CONNECTED, + strlen(CUS_EVT_AP_CONNECTED))) { + printf("EVENT: AP_CONNECTED\n"); + print_mac((t_u8 *)event + strlen(CUS_EVT_AP_CONNECTED)); + printf("\n"); + break; + } +#define CUS_EVT_ADHOC_LINK_SENSED "EVENT=ADHOC_LINK_SENSED" + if (!strncmp + ((char *)event, CUS_EVT_ADHOC_LINK_SENSED, + strlen(CUS_EVT_ADHOC_LINK_SENSED))) { + printf("EVENT: ADHOC_LINK_SENSED\n"); + break; + } +#define CUS_EVT_ADHOC_LINK_LOST "EVENT=ADHOC_LINK_LOST" + if (!strncmp + ((char *)event, CUS_EVT_ADHOC_LINK_LOST, + strlen(CUS_EVT_ADHOC_LINK_LOST))) { + printf("EVENT: ADHOC_LINK_LOST\n"); + break; + } +#define CUS_EVT_OBSS_SCAN_PARAM "EVENT=OBSS_SCAN_PARAM" + if (!strncmp + ((char *)event, CUS_EVT_OBSS_SCAN_PARAM, + strlen(CUS_EVT_OBSS_SCAN_PARAM))) { + printf("EVENT: OBSS_SCAN_PARAM\n"); + break; + } +#define CUS_EVT_BW_CHANGED "EVENT=BW_CHANGED" + if (!strncmp + ((char *)event, CUS_EVT_BW_CHANGED, + strlen(CUS_EVT_BW_CHANGED))) { + printf("EVENT: BW_CHANGED\n"); + break; + } +#define CUS_EVT_MLME_MIC_ERR_UNI "MLME-MICHAELMICFAILURE.indication unicast" + if (!strncmp + ((char *)event, CUS_EVT_MLME_MIC_ERR_UNI, + strlen(CUS_EVT_MLME_MIC_ERR_UNI))) { + printf("EVENT: MLME-MICHAELMICFAILURE.indication unicast\n"); + break; + } +#define CUS_EVT_MLME_MIC_ERR_MUL "MLME-MICHAELMICFAILURE.indication multicast" + if (!strncmp + ((char *)event, CUS_EVT_MLME_MIC_ERR_MUL, + strlen(CUS_EVT_MLME_MIC_ERR_MUL))) { + printf("EVENT: MLME-MICHAELMICFAILURE.indication multicast\n"); + break; + } +#define CUS_EVT_BEACON_RSSI_LOW "EVENT=BEACON_RSSI_LOW" + if (!strncmp + ((char *)event, CUS_EVT_BEACON_RSSI_LOW, + strlen(CUS_EVT_BEACON_RSSI_LOW))) { + printf("EVENT: BEACON_RSSI_LOW\n"); + break; + } +#define CUS_EVT_BEACON_RSSI_HIGH "EVENT=BEACON_RSSI_HIGH" + if (!strncmp + ((char *)event, CUS_EVT_BEACON_RSSI_HIGH, + strlen(CUS_EVT_BEACON_RSSI_HIGH))) { + printf("EVENT: BEACON_RSSI_HIGH\n"); + break; + } +#define CUS_EVT_BEACON_SNR_LOW "EVENT=BEACON_SNR_LOW" + if (!strncmp + ((char *)event, CUS_EVT_BEACON_SNR_LOW, + strlen(CUS_EVT_BEACON_SNR_LOW))) { + printf("EVENT: BEACON_SNR_LOW\n"); + break; + } +#define CUS_EVT_BEACON_SNR_HIGH "EVENT=BEACON_SNR_HIGH" + if (!strncmp + ((char *)event, CUS_EVT_BEACON_SNR_HIGH, + strlen(CUS_EVT_BEACON_SNR_HIGH))) { + printf("EVENT: BEACON_SNR_HIGH\n"); + break; + } +#define CUS_EVT_MAX_FAIL "EVENT=MAX_FAIL" + if (!strncmp + ((char *)event, CUS_EVT_MAX_FAIL, + strlen(CUS_EVT_MAX_FAIL))) { + printf("EVENT: MAX_FAIL\n"); + break; + } +#define CUS_EVT_DATA_RSSI_LOW "EVENT=DATA_RSSI_LOW" + if (!strncmp + ((char *)event, CUS_EVT_DATA_RSSI_LOW, + strlen(CUS_EVT_DATA_RSSI_LOW))) { + printf("EVENT: DATA_RSSI_LOW\n"); + break; + } +#define CUS_EVT_DATA_SNR_LOW "EVENT=DATA_SNR_LOW" + if (!strncmp + ((char *)event, CUS_EVT_DATA_SNR_LOW, + strlen(CUS_EVT_DATA_SNR_LOW))) { + printf("EVENT: DATA_SNR_LOW\n"); + break; + } +#define CUS_EVT_DATA_RSSI_HIGH "EVENT=DATA_RSSI_HIGH" + if (!strncmp + ((char *)event, CUS_EVT_DATA_RSSI_HIGH, + strlen(CUS_EVT_DATA_RSSI_HIGH))) { + printf("EVENT: DATA_RSSI_HIGH\n"); + break; + } +#define CUS_EVT_DATA_SNR_HIGH "EVENT=DATA_SNR_HIGH" + if (!strncmp + ((char *)event, CUS_EVT_DATA_SNR_HIGH, + strlen(CUS_EVT_DATA_SNR_HIGH))) { + printf("EVENT: DATA_SNR_HIGH\n"); + break; + } +#define CUS_EVT_LINK_QUALITY "EVENT=LINK_QUALITY" + if (!strncmp + ((char *)event, CUS_EVT_LINK_QUALITY, + strlen(CUS_EVT_LINK_QUALITY))) { + printf("EVENT: LINK_QUALITY\n"); + break; + } +#define CUS_EVT_WEP_ICV_ERR "EVENT=WEP_ICV_ERR" + if (!strncmp + ((char *)event, CUS_EVT_WEP_ICV_ERR, + strlen(CUS_EVT_WEP_ICV_ERR))) { + printf("EVENT: WEP_ICV_ERR\n"); + break; + } +#define CUS_EVT_CHANNEL_SWITCH_ANN "EVENT=CHANNEL_SWITCH_ANN" + if (!strncmp + ((char *)event, CUS_EVT_CHANNEL_SWITCH_ANN, + strlen(CUS_EVT_CHANNEL_SWITCH_ANN))) { + printf("EVENT: CHANNEL_SWITCH_ANN\n"); + break; + } +#define CUS_EVT_HS_WAKEUP "HS_WAKEUP" + if (!strncmp + ((char *)event, CUS_EVT_HS_WAKEUP, + strlen(CUS_EVT_HS_WAKEUP))) { + printf("EVENT: HS_WAKEUP\n"); + break; + } +#define CUS_EVT_HS_ACTIVATED "HS_ACTIVATED" + if (!strncmp + ((char *)event, CUS_EVT_HS_ACTIVATED, + strlen(CUS_EVT_HS_ACTIVATED))) { + printf("EVENT: HS_ACTIVATED\n"); + break; + } +#define CUS_EVT_HS_DEACTIVATED "HS_DEACTIVATED" + if (!strncmp + ((char *)event, CUS_EVT_HS_DEACTIVATED, + strlen(CUS_EVT_HS_DEACTIVATED))) { + printf("EVENT: HS_DEACTIVATED\n"); + break; + } +/** Custom indication message sent to the application layer for WMM changes */ +#define WMM_CONFIG_CHANGE_INDICATION "WMM_CONFIG_CHANGE.indication" + if (!strncmp + ((char *)event, WMM_CONFIG_CHANGE_INDICATION, + strlen(WMM_CONFIG_CHANGE_INDICATION))) { + printf("EVENT: STA_DISCONNECTED\n"); + break; + } +#define CUS_EVT_DRIVER_HANG "EVENT=DRIVER_HANG" + if (!strncmp + ((char *)event, CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG))) { + printf("EVENT: DRIVER_HANG\n"); + break; + } + printf("ERR:Undefined event type (0x%X). Dumping event buffer:\n", (unsigned int)event_id); + hexdump((void *)event, size, ' '); + break; + } + return; +} + +/** + * @brief Read event data from netlink socket + * + * @param sk_fd Netlink socket handler + * @param buffer Pointer to the data buffer + * @param nlh Pointer to netlink message header + * @param msg Pointer to message header + * @return Number of bytes read or MLAN_EVENT_FAILURE + */ +int +read_event_netlink_socket(int sk_fd, unsigned char *buffer, + struct nlmsghdr *nlh, struct msghdr *msg) +{ + int count = -1; + count = recvmsg(sk_fd, msg, 0); +#if DEBUG + printf("DBG:Waiting for message from NETLINK.\n"); +#endif + if (count < 0) { + printf("ERR:NETLINK read failed!\n"); + terminate_flag++; + return MLAN_EVENT_FAILURE; + } +#if DEBUG + printf("DBG:Received message payload (%d)\n", count); +#endif + if (count > NLMSG_SPACE(NL_MAX_PAYLOAD)) { + printf("ERR:Buffer overflow!\n"); + return MLAN_EVENT_FAILURE; + } + memset(buffer, 0, NL_MAX_PAYLOAD); + memcpy(buffer, NLMSG_DATA(nlh), count - NLMSG_HDRLEN); +#if DEBUG + hexdump(buffer, count - NLMSG_HDRLEN, ' '); +#endif + return count - NLMSG_HDRLEN; +} + +/** + * @brief Configure and read event data from netlink socket + * + * @param sk_fd Array of netlink sockets + * @param no_of_sk Number of netlink sockets opened + * @param recv_buf Pointer to the array of evt_buf structures + * @param timeout Socket listen timeout value + * @param nlh Pointer to netlink message header + * @param msg Pointer to message header + * @return Number of bytes read or MLAN_EVENT_FAILURE + */ +int +read_event(int *sk_fd, int no_of_sk, evt_buf *recv_buf, int timeout, + struct nlmsghdr *nlh, struct msghdr *msg) +{ + struct timeval tv; + fd_set rfds; + int i = 0, max_sk_fd = sk_fd[0]; + int ret = MLAN_EVENT_FAILURE; + + /* Setup read fds */ + FD_ZERO(&rfds); + for (i = 0; i < no_of_sk; i++) { + if (sk_fd[i] > max_sk_fd) + max_sk_fd = sk_fd[i]; + + if (sk_fd[i] > 0) + FD_SET(sk_fd[i], &rfds); + } + + /* Initialize timeout value */ + if (timeout != 0) + tv.tv_sec = timeout; + else + tv.tv_sec = UAP_RECV_WAIT_DEFAULT; + tv.tv_usec = 0; + + /* Wait for reply */ + ret = select(max_sk_fd + 1, &rfds, NULL, NULL, &tv); + if (ret == -1) { + /* Error */ + terminate_flag++; + return MLAN_EVENT_FAILURE; + } else if (!ret) { + /* Timeout. Try again */ + return MLAN_EVENT_FAILURE; + } + for (i = 0; i < no_of_sk; i++) { + if (sk_fd[i] > 0) { + if (FD_ISSET(sk_fd[i], &rfds)) { + /* Success */ + recv_buf[i].flag = 1; + recv_buf[i].length = + read_event_netlink_socket(sk_fd[i], + recv_buf[i]. + buffer, nlh, + msg); + ret += recv_buf[i].length; + } + } + } + return ret; +} + +/* Command line options */ +static const struct option long_opts[] = { + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'v'}, + {NULL, 0, NULL, 0} +}; + +/** + * @brief Determine the netlink number + * + * @param i socket number + * + * @return Netlink number to use + */ +static int +get_netlink_num(int i) +{ + FILE *fp; + int netlink_num = NETLINK_MARVELL; + char str[64]; + char *srch = "netlink_num"; + char filename[64]; + + if (i == 0) { + strcpy(filename, "/proc/mwlan/config"); + } else if (i > 0) { + sprintf(filename, "/proc/mwlan/config%d", i); + } + /* Try to open /proc/mwlan/config$ */ + fp = fopen(filename, "r"); + if (fp) { + while (!feof(fp)) { + fgets(str, sizeof(str), fp); + if (strncmp(str, srch, strlen(srch)) == 0) { + netlink_num = atoi(str + strlen(srch) + 1); + break; + } + } + fclose(fp); + } else { + return -1; + } + printf("Netlink number = %d\n", netlink_num); + return netlink_num; +} + +/**************************************************************************** + Global functions +****************************************************************************/ +/** + * @brief The main function + * + * @param argc Number of arguments + * @param argv Pointer to the arguments + * @return 0 or 1 + */ +int +main(int argc, char *argv[]) +{ + int opt; + int nl_sk[MAX_NO_OF_DEVICES]; + struct nlmsghdr *nlh = NULL; + struct sockaddr_nl src_addr, dest_addr; + struct msghdr msg; + struct iovec iov; + evt_buf evt_recv_buf[MAX_NO_OF_DEVICES]; + struct timeval current_time; + struct tm *timeinfo; + int num_events = 0; + event_header *event = NULL; + int ret = MLAN_EVENT_FAILURE; + int netlink_num[MAX_NO_OF_DEVICES]; + char if_name[IFNAMSIZ + 1]; + t_u32 event_id = 0; + int i = 0, no_of_sk = 0, dev_index = -1; + + /* Check command line options */ + while ((opt = getopt_long(argc, argv, "hvti:", long_opts, NULL)) > 0) { + switch (opt) { + case 'h': + print_usage(); + return 0; + case 'v': + printf("mlanevent version : %s\n", MLAN_EVENT_VERSION); + return 0; + break; + case 'i': + if ((IS_HEX_OR_DIGIT(argv[2]) == MLAN_EVENT_FAILURE) || + (A2HEXDECIMAL(argv[2]) < 0) + || ((A2HEXDECIMAL(argv[2]) >= MAX_NO_OF_DEVICES) && + (A2HEXDECIMAL(argv[2]) != 0xff))) { + print_usage(); + return 1; + } else { + dev_index = A2HEXDECIMAL(argv[2]); + argc -= optind; + argv += optind; + } + break; + default: + print_usage(); + return 1; + } + } + + if (optind < argc) { + fputs("Too many arguments.\n", stderr); + print_usage(); + return 1; + } + if ((dev_index >= 0) && (dev_index < MAX_NO_OF_DEVICES)) { + no_of_sk = 1; + } else { + /* Currently, we support maximum 4 devices */ + /* TODO: determine no_of_sk at run time */ + no_of_sk = MAX_NO_OF_DEVICES; + } + + for (i = 0; i < no_of_sk; i++) { + /* Initialise */ + nl_sk[i] = -1; + if (no_of_sk == 1) { + netlink_num[i] = get_netlink_num(dev_index); + if (netlink_num[i] < 0) { + printf("ERR:Could not get netlink socket. Invalid device number.\n"); + ret = MLAN_EVENT_FAILURE; + goto done; + } + } else { + netlink_num[i] = get_netlink_num(i); + } + if (netlink_num[i] >= 0) { + /* Open netlink socket */ + nl_sk[i] = socket(PF_NETLINK, SOCK_RAW, netlink_num[i]); + if (nl_sk[i] < 0) { + printf("ERR:Could not open netlink socket.\n"); + ret = MLAN_EVENT_FAILURE; + goto done; + } + + /* Set source address */ + memset(&src_addr, 0, sizeof(src_addr)); + src_addr.nl_family = AF_NETLINK; + src_addr.nl_pid = getpid(); /* Our PID */ + src_addr.nl_groups = NL_MULTICAST_GROUP; + + /* Bind socket with source address */ + if (bind + (nl_sk[i], (struct sockaddr *)&src_addr, + sizeof(src_addr)) < 0) { + printf("ERR:Could not bind socket!\n"); + ret = MLAN_EVENT_FAILURE; + goto done; + } + + /* Set destination address */ + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.nl_family = AF_NETLINK; + dest_addr.nl_pid = 0; /* Kernel */ + dest_addr.nl_groups = NL_MULTICAST_GROUP; + + /* Initialize netlink header */ + nlh = (struct nlmsghdr *) + malloc(NLMSG_SPACE(NL_MAX_PAYLOAD)); + if (!nlh) { + printf("ERR: Could not alloc buffer\n"); + ret = MLAN_EVENT_FAILURE; + goto done; + } + memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD)); + + /* Initialize I/O vector */ + iov.iov_base = (void *)nlh; + iov.iov_len = NLMSG_SPACE(NL_MAX_PAYLOAD); + + /* Initialize message header */ + memset(&msg, 0, sizeof(struct msghdr)); + msg.msg_name = (void *)&dest_addr; + msg.msg_namelen = sizeof(dest_addr); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + memset(&evt_recv_buf[i], 0, sizeof(evt_buf)); + } + } + gettimeofday(¤t_time, NULL); + + printf("\n"); + printf("**********************************************\n"); + if ((timeinfo = localtime(&(current_time.tv_sec)))) + printf("mlanevent start time : %s", asctime(timeinfo)); + printf(" %u usecs\n", + (unsigned int)current_time.tv_usec); + printf("**********************************************\n"); + + signal(SIGTERM, sig_handler); + signal(SIGINT, sig_handler); + signal(SIGALRM, sig_handler); + while (1) { + if (terminate_flag) { + printf("Stopping!\n"); + break; + } + ret = read_event(nl_sk, no_of_sk, evt_recv_buf, 0, nlh, &msg); + + /* No result. Loop again */ + if (ret == MLAN_EVENT_FAILURE) { + continue; + } + if (ret == 0) { + /* Zero bytes received */ + printf("ERR:Received zero bytes!\n"); + continue; + } + for (i = 0; i < no_of_sk; i++) { + if (evt_recv_buf[i].flag == 1) { + num_events++; + gettimeofday(¤t_time, NULL); + printf("\n"); + printf("============================================\n"); + printf("Received event"); + if ((timeinfo = + localtime(&(current_time.tv_sec)))) + printf(": %s", asctime(timeinfo)); + printf(" %u usecs\n", + (unsigned int)current_time.tv_usec); + printf("============================================\n"); + + memcpy(&event_id, evt_recv_buf[i].buffer, + sizeof(event_id)); + if (((event_id & 0xFF000000) == 0x80000000) || + ((event_id & 0xFF000000) == 0)) { + event = (event_header + *)(evt_recv_buf[i].buffer); + } else { + memset(if_name, 0, IFNAMSIZ + 1); + memcpy(if_name, evt_recv_buf[i].buffer, + IFNAMSIZ); + printf("EVENT for interface %s\n", + if_name); + event = (event_header + *)((t_u8 *)(evt_recv_buf[i]. + buffer) + + IFNAMSIZ); + ret -= IFNAMSIZ; + evt_recv_buf[i].length -= IFNAMSIZ; + } +#if DEBUG + printf("DBG:Received buffer =\n"); + hexdump(evt_recv_buf[i].buffer, + evt_recv_buf[i].length + IFNAMSIZ, ' '); +#endif + print_event(event, evt_recv_buf[i].length); + /* Reset event flag after reading */ + evt_recv_buf[i].flag = 0; + } + } + fflush(stdout); + } + gettimeofday(¤t_time, NULL); + printf("\n"); + printf("*********************************************\n"); + if ((timeinfo = localtime(&(current_time.tv_sec)))) + printf("mlanevent end time : %s", asctime(timeinfo)); + printf(" %u usecs\n", + (unsigned int)current_time.tv_usec); + printf("Total events : %u\n", num_events); + printf("*********************************************\n"); +done: + for (i = 0; i < no_of_sk; i++) { + if (nl_sk[i] > 0) + close(nl_sk[i]); + } + if (nlh) + free(nlh); + return 0; +}
diff --git a/wlan_sd8897/mapp/mlanevent/mlanevent.h b/wlan_sd8897/mapp/mlanevent/mlanevent.h new file mode 100644 index 0000000..c3b2c67 --- /dev/null +++ b/wlan_sd8897/mapp/mlanevent/mlanevent.h
@@ -0,0 +1,1375 @@ +/** @file mlanevent.h + * + * @brief Header file for mlanevent application + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available along with the File in the gpl.txt file or by writing to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 03/18/08: Initial creation +************************************************************************/ + +#ifndef _MLAN_EVENT_H +#define _MLAN_EVENT_H + +/** Character, 1 byte */ +typedef signed char t_s8; +/** Unsigned character, 1 byte */ +typedef unsigned char t_u8; + +/** Short integer */ +typedef signed short t_s16; +/** Unsigned short integer */ +typedef unsigned short t_u16; + +/** Integer */ +typedef signed int t_s32; +/** Unsigned integer */ +typedef unsigned int t_u32; + +/** Long long integer */ +typedef long long t_s64; +/** Unsigned long long integer */ +typedef unsigned long long t_u64; + +#if (BYTE_ORDER == LITTLE_ENDIAN) +#undef BIG_ENDIAN_SUPPORT +#endif + +/** 16 bits byte swap */ +#define swap_byte_16(x) \ + ((t_u16)((((t_u16)(x) & 0x00ffU) << 8) | \ + (((t_u16)(x) & 0xff00U) >> 8))) + +/** 32 bits byte swap */ +#define swap_byte_32(x) \ + ((t_u32)((((t_u32)(x) & 0x000000ffUL) << 24) | \ + (((t_u32)(x) & 0x0000ff00UL) << 8) | \ + (((t_u32)(x) & 0x00ff0000UL) >> 8) | \ + (((t_u32)(x) & 0xff000000UL) >> 24))) + +/** 64 bits byte swap */ +#define swap_byte_64(x) \ + ((t_u64)((t_u64)(((t_u64)(x) & 0x00000000000000ffULL) << 56) | \ + (t_u64)(((t_u64)(x) & 0x000000000000ff00ULL) << 40) | \ + (t_u64)(((t_u64)(x) & 0x0000000000ff0000ULL) << 24) | \ + (t_u64)(((t_u64)(x) & 0x00000000ff000000ULL) << 8) | \ + (t_u64)(((t_u64)(x) & 0x000000ff00000000ULL) >> 8) | \ + (t_u64)(((t_u64)(x) & 0x0000ff0000000000ULL) >> 24) | \ + (t_u64)(((t_u64)(x) & 0x00ff000000000000ULL) >> 40) | \ + (t_u64)(((t_u64)(x) & 0xff00000000000000ULL) >> 56) )) + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert from 16 bit little endian format to CPU format */ +#define uap_le16_to_cpu(x) swap_byte_16(x) +/** Convert from 32 bit little endian format to CPU format */ +#define uap_le32_to_cpu(x) swap_byte_32(x) +/** Convert from 64 bit little endian format to CPU format */ +#define uap_le64_to_cpu(x) swap_byte_64(x) +/** Convert to 16 bit little endian format from CPU format */ +#define uap_cpu_to_le16(x) swap_byte_16(x) +/** Convert to 32 bit little endian format from CPU format */ +#define uap_cpu_to_le32(x) swap_byte_32(x) +/** Convert to 64 bit little endian format from CPU format */ +#define uap_cpu_to_le64(x) swap_byte_64(x) +#else /* BIG_ENDIAN_SUPPORT */ +/** Do nothing */ +#define uap_le16_to_cpu(x) x +/** Do nothing */ +#define uap_le32_to_cpu(x) x +/** Do nothing */ +#define uap_le64_to_cpu(x) x +/** Do nothing */ +#define uap_cpu_to_le16(x) x +/** Do nothing */ +#define uap_cpu_to_le32(x) x +/** Do nothing */ +#define uap_cpu_to_le64(x) x +#endif /* BIG_ENDIAN_SUPPORT */ + +/** Convert WPS TLV header from network to host order */ +#define endian_convert_tlv_wps_header_in(t,l) \ + { \ + (t) = ntohs(t); \ + (l) = ntohs(l); \ + } + +/** + * Hex or Decimal to Integer + * @param num string to convert into decimal or hex + */ +#define A2HEXDECIMAL(num) \ + (strncasecmp("0x", (num), 2)?(unsigned int) strtoll((num),NULL,0):a2hex((num)))\ + +/** + * Check of decimal or hex string + * @param num string + */ +#define IS_HEX_OR_DIGIT(num) \ + (strncasecmp("0x", (num), 2)?ISDIGIT((num)):ishexstring((num)))\ + +/** MLan Event application version string */ +#define MLAN_EVENT_VERSION "MlanEvent 2.0" + +/** Failure */ +#define MLAN_EVENT_FAILURE -1 + +#ifdef __GNUC__ +/** Structure packing begins */ +#define PACK_START +/** Structure packeing end */ +#define PACK_END __attribute__ ((packed)) +#else +/** Structure packing begins */ +#define PACK_START __packed +/** Structure packeing end */ +#define PACK_END +#endif + +#ifndef ETH_ALEN +/** MAC address length */ +#define ETH_ALEN 6 +#endif + +/** Netlink protocol number */ +#define NETLINK_MARVELL (MAX_LINKS - 1) +/** Netlink maximum payload size */ +#define NL_MAX_PAYLOAD 1024 +/** Netlink multicast group number */ +#define NL_MULTICAST_GROUP 1 +/** Default wait time in seconds for events */ +#define UAP_RECV_WAIT_DEFAULT 10 +/** Maximum number of devices */ +#define MAX_NO_OF_DEVICES 4 + +#ifndef NLMSG_HDRLEN +/** NL message header length */ +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#endif + +/** Event ID mask */ +#define EVENT_ID_MASK 0xffff + +/** BSS number mask */ +#define BSS_NUM_MASK 0xf + +/** Get BSS number from event cause (bit 23:16) */ +#define EVENT_GET_BSS_NUM(event_cause) \ + (((event_cause) >> 16) & BSS_NUM_MASK) + +/** Invitation Flag mask */ +#define INVITATION_FLAG_MASK 0x01 + +/* Event buffer */ +typedef PACK_START struct _evt_buf { + /** Flag to check if event data is present in the buffer or not */ + int flag; + /** Event length */ + int length; + /** Event data */ + t_u8 buffer[NL_MAX_PAYLOAD]; +} PACK_END evt_buf; + +/** Event header */ +typedef PACK_START struct _event_header { + /** Event ID */ + t_u32 event_id; + /** Event data */ + t_u8 event_data[0]; +} PACK_END event_header; + +/** Event ID length */ +#define EVENT_ID_LEN 4 + +/** Event ID : WMM status change */ +#define MICRO_AP_EV_WMM_STATUS_CHANGE 0x00000017 + +/** Event ID: STA deauth */ +#define MICRO_AP_EV_ID_STA_DEAUTH 0x0000002c + +/** Event ID: STA associated */ +#define MICRO_AP_EV_ID_STA_ASSOC 0x0000002d + +/** Event ID: BSS started */ +#define MICRO_AP_EV_ID_BSS_START 0x0000002e + +/** Event ID: Debug event */ +#define MICRO_AP_EV_ID_DEBUG 0x00000036 + +/** Event ID: BSS idle event */ +#define MICRO_AP_EV_BSS_IDLE 0x00000043 + +/** Event ID: BSS active event */ +#define MICRO_AP_EV_BSS_ACTIVE 0x00000044 + +/** Event ID: WEP ICV error */ +#define EVENT_WEP_ICV_ERROR 0x00000046 + +#ifdef WIFI_DIRECT_SUPPORT +/** Event ID: UAP,STA wifidirect generic event */ +#define EVENT_WIFIDIRECT_GENERIC 0x00000049 + +/** Event ID: UAP,STA wifidirect service discovery event */ +#define EVENT_WIFIDIRECT_SERVICE_DISCOVERY 0x0000004a +#endif + +/** Event ID: RSN Connect event */ +#define MICRO_AP_EV_RSN_CONNECT 0x00000051 + +/** Event ID: TDLS generic event */ +#define EVENT_TDLS_GENERIC 0x00000052 + +/** event type for tdls setup failure */ +#define TDLS_EVENT_TYPE_SETUP_FAILURE 1 +/** event type for tdls setup request received */ +#define TDLS_EVENT_TYPE_SETUP_REQ_RCVD 2 +/** event type for tdls link torn down */ +#define TDLS_EVENT_TYPE_LINK_TORN_DOWN 3 +/** event type for tdls link established */ +#define TDLS_EVENT_TYPE_LINK_ESTABLISHED 4 +/** event type for tdls debug */ +#define TDLS_EVENT_TYPE_DEBUG 5 +/** event type for tdls packet */ +#define TDLS_EVENT_TYPE_PACKET 6 +/** event type for channel switch result */ +#define TDLS_EVENT_TYPE_CHAN_SWITCH_RESULT 7 +/** event type for start channel switch */ +#define TDLS_EVENT_TYPE_START_CHAN_SWITCH 8 +/** event type for stop channel switch */ +#define TDLS_EVENT_TYPE_CHAN_SWITCH_STOPPED 9 + +/** Event ID: Radar Detected */ +#define EVENT_RADAR_DETECTED 0x00000053 +/** Event ID: Channel Report Ready */ +#define EVENT_CHANNEL_REPORT_RDY 0x00000054 + +/** Event ID: NLIST REPORT */ +#define EVENT_NLIST_REPORT 0x00000079 +/** NLIST_REPORT TLV */ +#define MRVL_NEIGHBOR_REPORT_TLV_ID 0x1de + +/** HS WAKE UP event id */ +#define UAP_EVENT_ID_HS_WAKEUP 0x80000001 +/** HS_ACTIVATED event id */ +#define UAP_EVENT_ID_DRV_HS_ACTIVATED 0x80000002 +/** HS DEACTIVATED event id */ +#define UAP_EVENT_ID_DRV_HS_DEACTIVATED 0x80000003 +/** HOST SLEEP AWAKE event id in legacy PS*/ +#define UAP_EVENT_HOST_SLEEP_AWAKE 0x00000012 + +/** HS WAKE UP event id */ +#define UAP_EVENT_ID_DRV_MGMT_FRAME 0x80000005 + +/** SCAN REPORT Event id */ +#define EVENT_ID_DRV_SCAN_REPORT 0x80000009 + +/** WPA IE Tag */ +#define IEEE_WPA_IE 221 +/** RSN IE Tag */ +#define IEEE_RSN_IE 48 + +/** TLV ID : WAPI Information */ +#define MRVL_WAPI_INFO_TLV_ID 0x0167 + +/** TLV ID : Management Frame */ +#define MRVL_MGMT_FRAME_TLV_ID 0x0168 + +/** TLV Id : Channel Config */ +#define MRVL_CHANNELCONFIG_TLV_ID 0x012a + +/** Assoc Request */ +#define SUBTYPE_ASSOC_REQUEST 0 +/** Assoc Response */ +#define SUBTYPE_ASSOC_RESPONSE 1 +/** ReAssoc Request */ +#define SUBTYPE_REASSOC_REQUEST 2 +/** ReAssoc Response */ +#define SUBTYPE_REASSOC_RESPONSE 3 +/** WEP key user input length */ +#define WEP_KEY_USER_INPUT 13 + +/** TLV buffer header*/ +typedef PACK_START struct _tlvbuf_header { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; +} PACK_END tlvbuf_header; + +/** Event body : STA deauth */ +typedef PACK_START struct _eventbuf_sta_deauth { + /** Deauthentication reason */ + t_u16 reason_code; + /** MAC address of deauthenticated STA */ + t_u8 sta_mac_address[ETH_ALEN]; +} PACK_END eventbuf_sta_deauth; + +/** Event body : WEP ICV error */ +typedef PACK_START struct _eventbuf_wep_icv_error { + /** Deauthentication reason */ + t_u16 reason_code; + /** MAC address of deauthenticated STA */ + t_u8 sta_mac_address[ETH_ALEN]; + /** WEP key index */ + t_u8 wep_key_index; + /** WEP key length */ + t_u8 wep_key_length; + /** WEP key */ + t_u8 wep_key[WEP_KEY_USER_INPUT]; +} PACK_END eventbuf_wep_icv_error; + +/** Event body : STA associated */ +typedef PACK_START struct _eventbuf_sta_assoc { + /** Reserved */ + t_u8 reserved[2]; + /** MAC address of associated STA */ + t_u8 sta_mac_address[ETH_ALEN]; + /** Assoc request/response buffer */ + t_u8 assoc_payload[0]; +} PACK_END eventbuf_sta_assoc; + +/** Event body : RSN Connect */ +typedef PACK_START struct _eventbuf_rsn_connect { + /** Reserved */ + t_u8 reserved[2]; + /** MAC address of Station */ + t_u8 sta_mac_address[ETH_ALEN]; + /** WPA/WPA2 TLV IEs */ + t_u8 tlv_list[0]; +} PACK_END eventbuf_rsn_connect; + +/** Event body : BSS started */ +typedef PACK_START struct _eventbuf_bss_start { + /** Reserved */ + t_u8 reserved[2]; + /** MAC address of BSS */ + t_u8 ap_mac_address[ETH_ALEN]; +} PACK_END eventbuf_bss_start; + +/** + * IEEE 802.11 MAC Message Data Structures + * + * Each IEEE 802.11 MAC message includes a MAC header, a frame body (which + * can be empty), and a frame check sequence field. This section gives the + * structures that used for the MAC message headers and frame bodies that + * can exist in the three types of MAC messages - 1) Control messages, + * 2) Data messages, and 3) Management messages. + */ +#ifdef BIG_ENDIAN_SUPPORT +typedef PACK_START struct _IEEEtypes_FrameCtl_t { + /** Order */ + t_u8 order:1; + /** Wep */ + t_u8 wep:1; + /** More Data */ + t_u8 more_data:1; + /** Power Mgmt */ + t_u8 pwr_mgmt:1; + /** Retry */ + t_u8 retry:1; + /** More Frag */ + t_u8 more_frag:1; + /** From DS */ + t_u8 from_ds:1; + /** To DS */ + t_u8 to_ds:1; + /** Sub Type */ + t_u8 sub_type:4; + /** Type */ + t_u8 type:2; + /** Protocol Version */ + t_u8 protocol_version:2; +} PACK_END IEEEtypes_FrameCtl_t; +#else +typedef PACK_START struct _IEEEtypes_FrameCtl_t { + /** Protocol Version */ + t_u8 protocol_version:2; + /** Type */ + t_u8 type:2; + /** Sub Type */ + t_u8 sub_type:4; + /** To DS */ + t_u8 to_ds:1; + /** From DS */ + t_u8 from_ds:1; + /** More Frag */ + t_u8 more_frag:1; + /** Retry */ + t_u8 retry:1; + /** Power Mgmt */ + t_u8 pwr_mgmt:1; + /** More Data */ + t_u8 more_data:1; + /** Wep */ + t_u8 wep:1; + /** Order */ + t_u8 order:1; +} PACK_END IEEEtypes_FrameCtl_t; +#endif + +/** IEEEtypes_AssocRqst_t */ +typedef PACK_START struct _IEEEtypes_AssocRqst_t { + /** Capability Info */ + t_u16 cap_info; + /** Listen Interval */ + t_u16 listen_interval; + /** IE Buffer */ + t_u8 ie_buffer[0]; +} PACK_END IEEEtypes_AssocRqst_t; + +/** IEEEtypes_AssocRsp_t */ +typedef PACK_START struct _IEEEtypes_AssocRsp_t { + /** Capability Info */ + t_u16 cap_info; + /** Status Code */ + t_u16 status_code; + /** AID */ + t_u16 aid; +} PACK_END IEEEtypes_AssocRsp_t; + +/** IEEEtypes_ReAssocRqst_t */ +typedef PACK_START struct _IEEEtypes_ReAssocRqst_t { + /** Capability Info */ + t_u16 cap_info; + /** Listen Interval */ + t_u16 listen_interval; + /** Current AP Address */ + t_u8 current_ap_addr[ETH_ALEN]; + /** IE Buffer */ + t_u8 ie_buffer[0]; +} PACK_END IEEEtypes_ReAssocRqst_t; + +/** channel band */ +enum { + BAND_2GHZ = 0, + BAND_5GHZ = 1, + BAND_4GHZ = 2, +}; + +/** channel offset */ +enum { + SEC_CHAN_NONE = 0, + SEC_CHAN_ABOVE = 1, + SEC_CHAN_5MHZ = 2, + SEC_CHAN_BELOW = 3 +}; + +/** channel bandwidth */ +enum { + CHAN_BW_20MHZ = 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, + CHAN_BW_80MHZ, +}; + +/** scan mode */ +enum { + SCAN_MODE_MANUAL = 0, + SCAN_MODE_ACS, + SCAN_MODE_USER, +}; + +/** Band_Config_t */ +typedef PACK_START struct _Band_Config_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=user*/ + t_u8 scanMode:2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset:2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth:2; + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand:2; +#else + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand:2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth:2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset:2; + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=Adoption mode*/ + t_u8 scanMode:2; +#endif +} PACK_END Band_Config_t; + +/** TLV buffer : Channel Config */ +typedef PACK_START struct _tlvbuf_channel_config { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** Band Configuration */ + Band_Config_t bandcfg; + /** Channel number */ + t_u8 chan_number; +} PACK_END tlvbuf_channel_config; + +/** MrvlIEtypes_WapiInfoSet_t */ +typedef PACK_START struct _MrvlIEtypes_WapiInfoSet_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** Multicast PN */ + t_u8 multicast_PN[16]; +} PACK_END MrvlIEtypes_WapiInfoSet_t; + +/** MrvlIETypes_MgmtFrameSet_t */ +typedef PACK_START struct _MrvlIETypes_MgmtFrameSet_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** Frame Control */ + IEEEtypes_FrameCtl_t frame_control; + /** Frame Contents */ + t_u8 frame_contents[0]; +} PACK_END MrvlIETypes_MgmtFrameSet_t; + +/** Debug Type : Event */ +#define DEBUG_TYPE_EVENT 0 +/** Debug Type : Info */ +#define DEBUG_TYPE_INFO 1 + +/** Major debug id: Authenticator */ +#define DEBUG_ID_MAJ_AUTHENTICATOR 1 +/** Minor debug id: PWK1 */ +#define DEBUG_MAJ_AUTH_MIN_PWK1 0 +/** Minor debug id: PWK2 */ +#define DEBUG_MAJ_AUTH_MIN_PWK2 1 +/** Minor debug id: PWK3 */ +#define DEBUG_MAJ_AUTH_MIN_PWK3 2 +/** Minor debug id: PWK4 */ +#define DEBUG_MAJ_AUTH_MIN_PWK4 3 +/** Minor debug id: GWK1 */ +#define DEBUG_MAJ_AUTH_MIN_GWK1 4 +/** Minor debug id: GWK2 */ +#define DEBUG_MAJ_AUTH_MIN_GWK2 5 +/** Minor debug id: station reject */ +#define DEBUG_MAJ_AUTH_MIN_STA_REJ 6 +/** Minor debug id: EAPOL_TR */ +#define DEBUG_MAJ_AUTH_MIN_EAPOL_TR 7 + +/** Major debug id: Assoicate agent */ +#define DEBUG_ID_MAJ_ASSOC_AGENT 2 +/** Minor debug id: WPA IE*/ +#define DEBUG_ID_MAJ_ASSOC_MIN_WPA_IE 0 +/** Minor debug id: station reject */ +#define DEBUG_ID_MAJ_ASSOC_MIN_STA_REJ 1 + +/** ether_hdr */ +typedef PACK_START struct { + /** Dest address */ + t_u8 da[ETH_ALEN]; + /** Src address */ + t_u8 sa[ETH_ALEN]; + /** Header type */ + t_u16 type; +} PACK_END ether_hdr_t; + +/** 8021x header */ +typedef PACK_START struct { + /** Protocol version*/ + t_u8 protocol_ver; + /** Packet type*/ + t_u8 pckt_type; + /** Packet len */ + t_u8 pckt_body_len; +} PACK_END hdr_8021x_t; + +/** Nonce size */ +#define NONCE_SIZE 32 +/** Max WPA IE len */ +#define MAX_WPA_IE_LEN 64 +/** EAPOL mic size */ +#define EAPOL_MIC_SIZE 16 + +/** EAPOL key message */ +typedef PACK_START struct { + /** Ether header */ + ether_hdr_t ether_hdr; + /** 8021x header */ + hdr_8021x_t hdr_8021x; + /** desc_type */ + t_u8 desc_type; + /** Key info */ + t_u16 k; + /** Key length */ + t_u16 key_length; + /** Replay count */ + t_u32 replay_cnt[2]; + /** Key nonce */ + t_u8 key_nonce[NONCE_SIZE]; + /** Key IV */ + t_u8 eapol_key_iv[16]; + /** Key RSC */ + t_u8 key_rsc[8]; + /** Key ID */ + t_u8 key_id[8]; + /** Key MIC */ + t_u8 key_mic[EAPOL_MIC_SIZE]; + /** Key len */ + t_u16 key_material_len; + /** Key data */ + t_u8 key_data[MAX_WPA_IE_LEN]; +} PACK_END eapol_keymsg_debug_t; + +/** Failure after receive EAPOL MSG2 PMK */ +#define REJECT_STATE_FAIL_EAPOL_2 1 +/** Failure after receive EAPOL MSG4 PMK*/ +#define REJECT_STATE_FAIL_EAPOL_4 2 +/** Failure after receive EAPOL Group MSG2 GWK */ +#define REJECT_STATE_FAIL_EAPOL_GROUP_2 3 + +/** Fail reason: Invalid ie */ +#define IEEEtypes_REASON_INVALID_IE 13 +/** Fail reason: Mic failure */ +#define IEEEtypes_REASON_MIC_FAILURE 14 + +/** Station reject */ +typedef PACK_START struct { + /** Reject state */ + t_u8 reject_state; + /** Reject reason */ + t_u16 reject_reason; + /** Station mac address */ + t_u8 sta_mac_addr[ETH_ALEN]; +} PACK_END sta_reject_t; + +/** wpa_ie */ +typedef PACK_START struct { + /** Station mac address */ + t_u8 sta_mac_addr[ETH_ALEN]; + /** WPA IE */ + t_u8 wpa_ie[MAX_WPA_IE_LEN]; +} PACK_END wpaie_t; + +/** Initial state of the state machine */ +#define EAPOL_START 1 +/** Sent eapol msg1, wait for msg2 from the client */ +#define EAPOL_WAIT_PWK2 2 +/** Sent eapol msg3, wait for msg4 from the client */ +#define EAPOL_WAIT_PWK4 3 +/** Sent eapol group key msg1, wait for group msg2 from the client */ +#define EAPOL_WAIT_GTK2 4 +/** Eapol handshake complete */ +#define EAPOL_END 5 + +#ifdef WIFI_DIRECT_SUPPORT +/** TLV : WifiDirect status */ +#define TLV_TYPE_WIFIDIRECT_STATUS 0x0000 +/** TLV : WifiDirect param capability */ +#define TLV_TYPE_WIFIDIRECT_CAPABILITY 0x0002 +/** TLV : WifiDirect param device Id */ +#define TLV_TYPE_WIFIDIRECT_DEVICE_ID 0x0003 +/** TLV : WifiDirect param group owner intent */ +#define TLV_TYPE_WIFIDIRECT_GROUPOWNER_INTENT 0x0004 +/** TLV : WifiDirect param config timeout */ +#define TLV_TYPE_WIFIDIRECT_CONFIG_TIMEOUT 0x0005 +/** TLV : WifiDirect param channel */ +#define TLV_TYPE_WIFIDIRECT_CHANNEL 0x0006 +/** TLV : WifiDirect param group bssId */ +#define TLV_TYPE_WIFIDIRECT_GROUP_BSS_ID 0x0007 +/** TLV : WifiDirect param extended listen time */ +#define TLV_TYPE_WIFIDIRECT_EXTENDED_LISTEN_TIME 0x0008 +/** TLV : WifiDirect param intended address */ +#define TLV_TYPE_WIFIDIRECT_INTENDED_ADDRESS 0x0009 +/** TLV : WifiDirect param manageability */ +#define TLV_TYPE_WIFIDIRECT_MANAGEABILITY 0x000a +/** TLV : WifiDirect param channel list */ +#define TLV_TYPE_WIFIDIRECT_CHANNEL_LIST 0x000b +/** TLV : WifiDirect Notice of Absence */ +#define TLV_TYPE_WIFIDIRECT_NOTICE_OF_ABSENCE 0x000c +/** TLV : WifiDirect param device Info */ +#define TLV_TYPE_WIFIDIRECT_DEVICE_INFO 0x000d +/** TLV : WifiDirect param Group Info */ +#define TLV_TYPE_WIFIDIRECT_GROUP_INFO 0x000e +/** TLV : WifiDirect param group Id */ +#define TLV_TYPE_WIFIDIRECT_GROUP_ID 0x000f +/** TLV : WifiDirect param interface */ +#define TLV_TYPE_WIFIDIRECT_INTERFACE 0x0010 +/** TLV : WifiDirect param operating channel */ +#define TLV_TYPE_WIFIDIRECT_OPCHANNEL 0x0011 +/** TLV : WifiDirect param invitation flag */ +#define TLV_TYPE_WIFIDIRECT_INVITATION_FLAG 0x0012 + +/** WPS Device Info OUI+Type+SubType Length */ +#define WPS_DEVICE_TYPE_LEN 8 + +/** IE header len */ +#define IE_HEADER_LEN 2 + +/** WIFIDIRECT IE header len */ +#define WIFIDIRECT_IE_HEADER_LEN 3 + +/** OUI Type WIFIDIRECT */ +#define WIFIDIRECT_OUI_TYPE 9 +/** OUI Type WPS */ +#define WIFI_WPS_OUI_TYPE 4 + +/* + * To handle overlapping WIFIDIRECT IEs + */ +/** IE next byte type */ +#define WIFIDIRECT_OVERLAP_TYPE 1 +/** IE next byte length */ +#define WIFIDIRECT_OVERLAP_LEN 2 +/** IE next byte data */ +#define WIFIDIRECT_OVERLAP_DATA 3 + +/** Max payload for IE buffer */ +#define WIFI_IE_MAX_PAYLOAD 256 + +/** Fixed length fields in bonjour payload query data */ +#define WIFIDIRECT_DISCOVERY_BONJOUR_FIXED_LEN 5 + +/** Fixed length fields in uPnP payload query data */ +#define WIFIDIRECT_DISCOVERY_UPNP_FIXED_LEN 3 + +/** Action field for discovery request */ +#define WIFIDIRECT_DISCOVERY_REQUEST_ACTION 10 + +/** Action field for discovery response */ +#define WIFIDIRECT_DISCOVERY_RESPONSE_ACTION 11 + +/** TLV buffer : WifiDirect Status */ +typedef PACK_START struct _tlvbuf_wifidirect_status { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT status code */ + t_u8 status_code; +} PACK_END tlvbuf_wifidirect_status; + +/** TLV buffer : wifidirect IE device Id */ +typedef PACK_START struct _tlvbuf_wifidirect_device_id { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT device MAC address */ + t_u8 dev_mac_address[ETH_ALEN]; +} PACK_END tlvbuf_wifidirect_device_id; + +/** TLV buffer : wifidirect IE capability */ +typedef PACK_START struct _tlvbuf_wifidirect_capability { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT device capability */ + t_u8 dev_capability; + /** WIFIDIRECT group capability */ + t_u8 group_capability; +} PACK_END tlvbuf_wifidirect_capability; + +/** TLV buffer : wifidirect IE Group owner intent */ +typedef PACK_START struct _tlvbuf_wifidirect_group_owner_intent { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT device group owner intent */ + t_u8 dev_intent; +} PACK_END tlvbuf_wifidirect_group_owner_intent; + +/** TLV buffer : WifiDirect IE Manageability */ +typedef PACK_START struct _tlvbuf_wifidirect_manageability { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT Manageability */ + t_u8 manageability; +} PACK_END tlvbuf_wifidirect_manageability; + +/** TLV buffer : WifiDirect IE Invitation Flag */ +typedef PACK_START struct _tlvbuf_wifidirect_invitation_flag { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT Manageability */ + t_u8 invitation_flag; +} PACK_END tlvbuf_wifidirect_invitation_flag; + +/** TLV buffer : wifidirect IE capability */ +typedef PACK_START struct _tlvbuf_wifidirect_channel { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT country string */ + t_u8 country_string[3]; + /** WIFIDIRECT regulatory class */ + t_u8 regulatory_class; + /** WIFIDIRECT channel number */ + t_u8 channel_number; +} PACK_END tlvbuf_wifidirect_channel; + +/** Channel Entry */ +typedef PACK_START struct _chan_entry { + /** WIFIDIRECT regulatory class */ + t_u8 regulatory_class; + /** WIFIDIRECT no of channels */ + t_u8 num_of_channels; + /** WIFIDIRECT channel number */ + t_u8 chan_list[0]; +} PACK_END chan_entry; + +/** TLV buffer : wifidirect IE channel list */ +typedef PACK_START struct _tlvbuf_wifidirect_channel_list { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT country string */ + t_u8 country_string[3]; + /** WIFIDIRECT channel entries */ + chan_entry wifidirect_chan_entry_list[0]; +} PACK_END tlvbuf_wifidirect_channel_list; + +/** NoA Descriptor */ +typedef PACK_START struct _noa_descriptor { + /** WIFIDIRECT count OR type */ + t_u8 count_type; + /** WIFIDIRECT duration */ + t_u32 duration; + /** WIFIDIRECT interval */ + t_u32 interval; + /** WIFIDIRECT start time */ + t_u32 start_time; +} PACK_END noa_descriptor; + +/** TLV buffer : WifiDirect IE Notice of Absence */ +typedef PACK_START struct _tlvbuf_wifidirect_notice_of_absence { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT NoA Index */ + t_u8 noa_index; + /** WIFIDIRECT CTWindow and OppPS parameters */ + t_u8 ctwindow_opp_ps; + /** WIFIDIRECT NoA Descriptor list */ + noa_descriptor wifidirect_noa_descriptor_list[0]; +} PACK_END tlvbuf_wifidirect_notice_of_absence; + +/** TLV buffer : wifidirect IE device Info */ +typedef PACK_START struct _tlvbuf_wifidirect_device_info { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT device address */ + t_u8 dev_address[ETH_ALEN]; + /** WPS config methods */ + t_u16 config_methods; + /** Primary device type : category */ + t_u16 primary_category; + /** Primary device type : OUI */ + t_u8 primary_oui[4]; + /** Primary device type : sub-category */ + t_u16 primary_subcategory; + /** Secondary Device Count */ + t_u8 secondary_dev_count; + /** Secondary Device Info */ + t_u8 secondary_dev_info[0]; + /** WPS Device Name Tag */ + t_u16 device_name_type; + /** WPS Device Name Length */ + t_u16 device_name_len; + /** Device name */ + t_u8 device_name[0]; +} PACK_END tlvbuf_wifidirect_device_info; + +typedef PACK_START struct _wifidirect_client_dev_info { + /** Length of each device */ + t_u8 dev_length; + /** WIFIDIRECT device address */ + t_u8 wifidirect_dev_address[ETH_ALEN]; + /** WIFIDIRECT Interface address */ + t_u8 wifidirect_intf_address[ETH_ALEN]; + /** WIFIDIRECT Device capability*/ + t_u8 wifidirect_dev_capability; + /** WPS config methods */ + t_u16 config_methods; + /** Primary device type : category */ + t_u16 primary_category; + /** Primary device type : OUI */ + t_u8 primary_oui[4]; + /** Primary device type : sub-category */ + t_u16 primary_subcategory; + /** Secondary Device Count */ + t_u8 wifidirect_secondary_dev_count; + /** Secondary Device Info */ + t_u8 wifidirect_secondary_dev_info[0]; + /** WPS WIFIDIRECT Device Name Tag */ + t_u16 wifidirect_device_name_type; + /** WPS WIFIDIRECT Device Name Length */ + t_u16 wifidirect_device_name_len; + /** WIFIDIRECT Device name */ + t_u8 wifidirect_device_name[0]; +} PACK_END wifidirect_client_dev_info; + +typedef PACK_START struct _tlvbuf_wifidirect_group_info { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** Secondary Device Info */ + t_u8 wifidirect_client_dev_list[0]; +} PACK_END tlvbuf_wifidirect_group_info; + +/** TLV buffer : wifidirect IE group Id */ +typedef PACK_START struct _tlvbuf_wifidirect_group_id { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT group MAC address */ + t_u8 group_address[ETH_ALEN]; + /** WIFIDIRECT group SSID */ + t_u8 group_ssid[0]; +} PACK_END tlvbuf_wifidirect_group_id; + +/** TLV buffer : wifidirect IE group BSS Id */ +typedef PACK_START struct _tlvbuf_wifidirect_group_bss_id { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT group Bss Id */ + t_u8 group_bssid[ETH_ALEN]; +} PACK_END tlvbuf_wifidirect_group_bss_id; + +/** TLV buffer : wifidirect IE Interface */ +typedef PACK_START struct _tlvbuf_wifidirect_interface { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT interface Id */ + t_u8 interface_id[ETH_ALEN]; + /** WIFIDIRECT interface count */ + t_u8 interface_count; + /** WIFIDIRECT interface addresslist */ + t_u8 interface_idlist[0]; +} PACK_END tlvbuf_wifidirect_interface; + +/** TLV buffer : WifiDirect configuration timeout */ +typedef PACK_START struct _tlvbuf_wifidirect_config_timeout { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** Group configuration timeout */ + t_u8 group_config_timeout; + /** Device configuration timeout */ + t_u8 device_config_timeout; +} PACK_END tlvbuf_wifidirect_config_timeout; + +/** TLV buffer : WifiDirect extended listen time */ +typedef PACK_START struct _tlvbuf_wifidirect_ext_listen_time { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** Availability period */ + t_u16 availability_period; + /** Availability interval */ + t_u16 availability_interval; +} PACK_END tlvbuf_wifidirect_ext_listen_time; + +/** TLV buffer : WifiDirect Intended Interface Address */ +typedef PACK_START struct _tlvbuf_wifidirect_intended_addr { + /** TLV Header tag */ + t_u8 tag; + /** TLV Header length */ + t_u16 length; + /** WIFIDIRECT Group interface address */ + t_u8 group_address[ETH_ALEN]; +} PACK_END tlvbuf_wifidirect_intended_addr; + +/** TLV buffer : Wifi WPS IE */ +typedef PACK_START struct _tlvbuf_wifi_wps_ie { + /** TLV Header tag */ + t_u16 tag; + /** TLV Header length */ + t_u16 length; + /** WIFI WPS IE data */ + t_u8 data[0]; +} PACK_END tlvbuf_wps_ie; + +/** WifiDirect IE Header */ +typedef PACK_START struct _wifidirect_ie_header { + /** Element ID */ + t_u8 element_id; + /** IE Length */ + t_u8 ie_length; + /** OUI */ + t_u8 oui[3]; + /** OUI type */ + t_u8 oui_type; + /** IE List of TLV */ + t_u8 ie_list[0]; +} PACK_END wifidirect_ie_header; + +/** Event : WifiDirect Generic event */ +typedef PACK_START struct _apeventbuf_wifidirect_generic { + /** Event Length */ + t_u16 event_length; + /** Event Type */ + t_u16 event_type; + /** Event SubType */ + t_u16 event_sub_type; + /** Peer mac address */ + t_u8 peer_mac_addr[ETH_ALEN]; + /** IE List of TLV */ + t_u8 entire_ie_list[0]; +} PACK_END apeventbuf_wifidirect_generic; + +/** Internal WIFIDIRECT structure for Query Data */ +typedef PACK_START struct wifidirect_query_data { + union { + PACK_START struct upnp_specific_query { + /** version field */ + t_u8 version; + /** value */ + t_u8 value[0]; + } PACK_END upnp; + + PACK_START struct bonjour_specific_query { + /** DNS name */ + t_u8 dns[0]; + /** DNS type */ + t_u8 dns_type; + /** version field */ + t_u8 version; + } PACK_END bonjour; + } u; +} PACK_END wifidirect_query_data; + +/** Internal WIFIDIRECT structure for Response Data */ +typedef PACK_START struct wifidirect_Response_data { + union { + PACK_START struct upnp_specific_response { + /** version field */ + t_u8 version; + /** value */ + t_u8 value[0]; + } PACK_END upnp; + + PACK_START struct bonjour_specific_response { + /** DNS name */ + t_u8 dns[0]; + /** DNS type */ + t_u8 dns_type; + /** version field */ + t_u8 version; + /** DNS name */ + t_u8 record[0]; + } PACK_END bonjour; + } u; +} PACK_END wifidirect_response_data; + +/** Event : Service Discovery request */ +typedef PACK_START struct _apeventbuf_wifidirect_discovery_request { + /** Peer mac address */ + t_u8 peer_mac_addr[ETH_ALEN]; + /** Category */ + t_u8 category; + /** Action */ + t_u8 action; + /** Dialog taken */ + t_u8 dialog_taken; + /** Advertize protocol IE */ + t_u8 advertize_protocol_ie[4]; + /** Query request Length */ + t_u16 query_len; + /** Information identifier */ + t_u8 info_id[2]; + /** Request Length */ + t_u16 request_len; + /** OUI */ + t_u8 oui[3]; + /** OUI sub type */ + t_u8 oui_sub_type; + /** Service update indicator */ + t_u16 service_update_indicator; + /** Vendor Length */ + t_u16 vendor_len; + /** Service protocol */ + t_u8 service_protocol; + /** Service transaction Id */ + t_u8 service_transaction_id; + /** Query Data */ + wifidirect_query_data disc_query; +} PACK_END apeventbuf_wifidirect_discovery_request; + +/** HostCmd_CMD_WIFIDIRECT_SERVICE_DISCOVERY response */ +typedef PACK_START struct _apeventbuf_wifidirect_discovery_response { + /** Peer mac address */ + t_u8 peer_mac_addr[ETH_ALEN]; + /** Category */ + t_u8 category; + /** Action */ + t_u8 action; + /** Dialog taken */ + t_u8 dialog_taken; + /** Status code */ + t_u8 status_code; + /** GAS comback reply */ + t_u16 gas_reply; + /** Advertize protocol IE */ + t_u8 advertize_protocol_ie[4]; + /** Query response Length */ + t_u16 query_len; + /** Information identifier */ + t_u8 info_id[2]; + /** Response Length */ + t_u16 response_len; + /** OUI */ + t_u8 oui[3]; + /** OUI sub type */ + t_u8 oui_sub_type; + /** Service update indicator */ + t_u16 service_update_indicator; + /** Vendor Length */ + t_u16 vendor_len; + /** Service protocol */ + t_u8 service_protocol; + /** Service transaction Id */ + t_u8 service_transaction_id; + /** Discovery status code */ + t_u8 disc_status_code; + /** Response Data */ + wifidirect_response_data disc_resp; +} PACK_END apeventbuf_wifidirect_discovery_response; + +/** enum : WPS attribute type */ +typedef enum { + SC_AP_Channel = 0x1001, + SC_Association_State = 0x1002, + SC_Authentication_Type = 0x1003, + SC_Authentication_Type_Flags = 0x1004, + SC_Authenticator = 0x1005, + SC_Config_Methods = 0x1008, + SC_Configuration_Error = 0x1009, + SC_Confirmation_URL4 = 0x100A, + SC_Confirmation_URL6 = 0x100B, + SC_Connection_Type = 0x100C, + SC_Connection_Type_Flags = 0x100D, + SC_Credential = 0x100E, + SC_Device_Name = 0x1011, + SC_Device_Password_ID = 0x1012, + SC_E_Hash1 = 0x1014, + SC_E_Hash2 = 0x1015, + SC_E_SNonce1 = 0x1016, + SC_E_SNonce2 = 0x1017, + SC_Encrypted_Settings = 0x1018, + SC_Encryption_Type = 0X100F, + SC_Encryption_Type_Flags = 0x1010, + SC_Enrollee_Nonce = 0x101A, + SC_Feature_ID = 0x101B, + SC_Identity = 0X101C, + SC_Identity_Proof = 0X101D, + SC_Key_Wrap_Authenticator = 0X101E, + SC_Key_Identifier = 0X101F, + SC_MAC_Address = 0x1020, + SC_Manufacturer = 0x1021, + SC_Message_Type = 0x1022, + SC_Model_Name = 0x1023, + SC_Model_Number = 0x1024, + SC_Network_Index = 0x1026, + SC_Network_Key = 0x1027, + SC_Network_Key_Index = 0x1028, + SC_New_Device_Name = 0x1029, + SC_New_Password = 0x102A, + SC_OOB_Device_Password = 0X102C, + SC_OS_Version = 0X102D, + SC_Power_Level = 0X102F, + SC_PSK_Current = 0x1030, + SC_PSK_Max = 0x1031, + SC_Public_Key = 0x1032, + SC_Radio_Enabled = 0x1033, + SC_Reboot = 0x1034, + SC_Registrar_Current = 0x1035, + SC_Registrar_Established = 0x1036, + SC_Registrar_List = 0x1037, + SC_Registrar_Max = 0x1038, + SC_Registrar_Nonce = 0x1039, + SC_Request_Type = 0x103A, + SC_Response_Type = 0x103B, + SC_RF_Band = 0X103C, + SC_R_Hash1 = 0X103D, + SC_R_Hash2 = 0X103E, + SC_R_SNonce1 = 0X103F, + SC_R_SNonce2 = 0x1040, + SC_Selected_Registrar = 0x1041, + SC_Serial_Number = 0x1042, + SC_Simple_Config_State = 0x1044, + SC_SSID = 0x1045, + SC_Total_Networks = 0x1046, + SC_UUID_E = 0x1047, + SC_UUID_R = 0x1048, + SC_Vendor_Extension = 0x1049, + SC_Version = 0x104A, + SC_X_509_Certificate_Request = 0x104B, + SC_X_509_Certificate = 0x104C, + SC_EAP_Identity = 0x104D, + SC_Message_Counter = 0x104E, + SC_Public_Key_Hash = 0x104F, + SC_Rekey_Key = 0x1050, + SC_Key_Lifetime = 0x1051, + SC_Permitted_Config_Methods = 0x1052, + SC_SelectedRegistrarConfigMethods = 0x1053, + SC_Primary_Device_Type = 0x1054, + SC_Secondary_Device_Type_List = 0x1055, + SC_Portable_Device = 0x1056, + SC_AP_Setup_Locked = 0x1057, + SC_Application_List = 0x1058, + SC_EAP_Type = 0x1059, + SC_Initialization_Vector = 0x1060, + SC_Key_Provided_Auto = 0x1061, + SC_8021x_Enabled = 0x1062, + SC_App_Session_key = 0x1063, + SC_WEP_Transmit_Key = 0x1064, +} wps_simple_config_attribute; +#endif + +/** structure for channel switch result from TDLS FW */ +typedef PACK_START struct _Channel_switch_result { + /** current channel, 0 - base channel, 1 - off channel*/ + t_u8 current_channel; + /** channel switch status*/ + t_u8 status; + /** channel switch fauilure reason code*/ + t_u8 reason; +} PACK_END Channel_switch_result; + +/** Event : TDLS Generic event */ +typedef PACK_START struct _eventbuf_tdls_generic { + /** Event Type */ + t_u16 event_type; + /** Peer mac address */ + t_u8 peer_mac_addr[ETH_ALEN]; + union { + /** channel switch result structure*/ + Channel_switch_result switch_result; + /** channel switch stop reason */ + t_u8 cs_stop_reason; + /** Reason code */ + t_u16 reason_code; + /** IE Length */ + t_u16 ie_length; + } u; + /** IE pointer */ + t_u8 ie_ptr[0]; +} PACK_END eventbuf_tdls_generic; + +/** Event : TDLS Debug event */ +typedef PACK_START struct _eventbuf_tdls_debug { + /** Event Type */ + t_u16 event_type; + /** TimeStamp value */ + t_u64 tsf; + /** First argument */ + t_u32 first_arg; + /** Second argument */ + t_u32 second_arg; + /** Third argument */ + t_u32 third_arg; + /** Third argument */ + t_u16 string_len; + /** Third argument */ + t_u8 string[0]; +} PACK_END eventbuf_tdls_debug; + +/** Event : TDLS packet event */ +typedef PACK_START struct _eventbuf_tdls_packet { + /** Event Type */ + t_u16 event_type; + /** Length */ + t_u16 length; + /** packet data */ + t_u8 data[0]; +} PACK_END eventbuf_tdls_packet; + +/** Eapol state */ +typedef PACK_START struct { + /** Eapol state*/ + t_u8 eapol_state; + /** Station address*/ + t_u8 sta_mac_addr[ETH_ALEN]; +} PACK_END eapol_state_t; + +/** Debug Info */ +typedef PACK_START union { + /** Eapol key message */ + eapol_keymsg_debug_t eapol_pwkmsg; + /** Station reject*/ + sta_reject_t sta_reject; + /** WPA IE */ + wpaie_t wpaie; + /** Eapol state */ + eapol_state_t eapol_state; +} PACK_END d_info; + +/** Event body : Debug */ +typedef PACK_START struct _eventbuf_debug { + /** Debug type */ + t_u8 debug_type; + /** Major debug id */ + t_u32 debug_id_major; + /** Minor debug id */ + t_u32 debug_id_minor; + /** Debug Info */ + d_info info; +} PACK_END eventbuf_debug; + +/** Event body : 11K NLIST */ +typedef PACK_START struct _nlist_entry_tlv { + tlvbuf_header hdr; + t_u8 bssid[6]; + t_u8 bssid_info[4]; + t_u8 reg_class; + t_u8 chan; + t_u8 phy_type; +} PACK_END nlist_entry_tlv; + +int ishexstring(void *hex); +unsigned int a2hex(char *s); +/** + * @brief isdigit for String. + * + * @param x Char string + * @return MLAN_EVENT_FAILURE for non-digit. + * 0 for digit + */ +static inline int +ISDIGIT(char *x) +{ + unsigned int i; + for (i = 0; i < strlen(x); i++) + if (isdigit(x[i]) == 0) + return MLAN_EVENT_FAILURE; + return 0; +} + +#endif /* _MLAN_EVENT_H */
diff --git a/wlan_sd8897/mapp/mlanutl/Makefile b/wlan_sd8897/mapp/mlanutl/Makefile new file mode 100644 index 0000000..b166f79 --- /dev/null +++ b/wlan_sd8897/mapp/mlanutl/Makefile
@@ -0,0 +1,54 @@ +# File : mlanutl/Makefile +# +# Copyright (C) 2011-2017, Marvell International Ltd. All Rights Reserved + +# Path to the top directory of the wlan distribution +PATH_TO_TOP = ../.. + +# Determine how we should copy things to the install directory +ABSPATH := $(filter /%, $(INSTALLDIR)) +RELPATH := $(filter-out /%, $(INSTALLDIR)) +INSTALLPATH := $(ABSPATH) +ifeq ($(strip $(INSTALLPATH)),) +INSTALLPATH := $(PATH_TO_TOP)/$(RELPATH) +endif + +# Override CFLAGS for application sources, remove __ kernel namespace defines +CFLAGS := $(filter-out -D__%, $(ccflags-y)) +# remove KERNEL include dir +CFLAGS := $(filter-out -I$(KERNELDIR)%, $(CFLAGS)) + + +#CFLAGS += -DAP22 -fshort-enums +CFLAGS += -Wall +#ECHO = @ +LIBS = -lrt + +.PHONY: default tags all + +OBJECTS = mlanutl.o +HEADERS = mlanutl.h + + + +exectarget=mlanutl +TARGET := $(exectarget) + +build default: $(TARGET) + @cp -f $(TARGET) $(INSTALLPATH) + +all : tags default + +$(TARGET): $(OBJECTS) $(HEADERS) + $(ECHO)$(CC) $(LIBS) -o $@ $(OBJECTS) + +%.o: %.c $(HEADERS) + $(ECHO)$(CC) $(CFLAGS) -c -o $@ $< + +tags: + ctags -R -f tags.txt + +distclean clean: + $(ECHO)$(RM) $(OBJECTS) $(TARGET) + $(ECHO)$(RM) tags.txt +
diff --git a/wlan_sd8897/mapp/mlanutl/mlanhostcmd.c b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.c new file mode 100644 index 0000000..e61220f --- /dev/null +++ b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.c
@@ -0,0 +1,921 @@ +/** @file mlanhostcmd.c + * + * @brief This file contains mlanutl helper functions + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 11/26/2008: initial version +************************************************************************/ + +#include "mlanhostcmd.h" + +#ifndef MIN +/** Find minimum value */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief get hostcmd data + * + * @param ln A pointer to line number + * @param buf A pointer to hostcmd data + * @param size A pointer to the return size of hostcmd buffer + * @return MLAN_STATUS_SUCCESS + */ +static int +mlan_get_hostcmd_data(FILE * fp, int *ln, t_u8 *buf, t_u16 *size) +{ + t_s32 errors = 0, i; + t_s8 line[256], *pos, *pos1, *pos2, *pos3; + t_u16 len; + + while ((pos = mlan_config_get_line(fp, line, sizeof(line), ln))) { + (*ln)++; + if (strcmp(pos, "}") == 0) { + break; + } + + pos1 = strchr(pos, ':'); + if (pos1 == NULL) { + printf("Line %d: Invalid hostcmd line '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos1++ = '\0'; + + pos2 = strchr(pos1, '='); + if (pos2 == NULL) { + printf("Line %d: Invalid hostcmd line '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos2++ = '\0'; + + len = a2hex_or_atoi(pos1); + if (len < 1 || len > MRVDRV_SIZE_OF_CMD_BUFFER) { + printf("Line %d: Invalid hostcmd line '%s'\n", *ln, + pos); + errors++; + continue; + } + + *size += len; + + if (*pos2 == '"') { + pos2++; + pos3 = strchr(pos2, '"'); + if (pos3 == NULL) { + printf("Line %d: invalid quotation '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos3 = '\0'; + memset(buf, 0, len); + memmove(buf, pos2, MIN(strlen(pos2), len)); + buf += len; + } else if (*pos2 == '\'') { + pos2++; + pos3 = strchr(pos2, '\''); + if (pos3 == NULL) { + printf("Line %d: invalid quotation '%s'\n", *ln, + pos); + errors++; + continue; + } + *pos3 = ','; + for (i = 0; i < len; i++) { + pos3 = strchr(pos2, ','); + if (pos3 != NULL) { + *pos3 = '\0'; + *buf++ = (t_u8)a2hex_or_atoi(pos2); + pos2 = pos3 + 1; + } else + *buf++ = 0; + } + } else if (*pos2 == '{') { + t_u16 tlvlen = 0, tmp_tlvlen; + mlan_get_hostcmd_data(fp, ln, buf + len, &tlvlen); + tmp_tlvlen = tlvlen; + while (len--) { + *buf++ = (t_u8)(tmp_tlvlen & 0xff); + tmp_tlvlen >>= 8; + } + *size += tlvlen; + buf += tlvlen; + } else { + t_u32 value = a2hex_or_atoi(pos2); + while (len--) { + *buf++ = (t_u8)(value & 0xff); + value >>= 8; + } + } + } + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief convert char to hex integer + * + * @param chr char to convert + * @return hex integer or 0 + */ +int +hexval(t_s32 chr) +{ + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + return 0; +} + +/** + * @brief Hump hex data + * + * @param prompt A pointer prompt buffer + * @param p A pointer to data buffer + * @param len the len of data buffer + * @param delim delim char + * @return hex integer + */ +t_void +hexdump(t_s8 *prompt, t_void *p, t_s32 len, t_s8 delim) +{ + t_s32 i; + t_u8 *s = p; + + if (prompt) { + printf("%s: len=%d\n", prompt, (int)len); + } + for (i = 0; i < len; i++) { + if (i != len - 1) + printf("%02x%c", *s++, delim); + else + printf("%02x\n", *s); + if ((i + 1) % 16 == 0) + printf("\n"); + } + printf("\n"); +} + +/** + * @brief convert char to hex integer + * + * @param chr char + * @return hex integer + */ +t_u8 +hexc2bin(t_s8 chr) +{ + if (chr >= '0' && chr <= '9') + chr -= '0'; + else if (chr >= 'A' && chr <= 'F') + chr -= ('A' - 10); + else if (chr >= 'a' && chr <= 'f') + chr -= ('a' - 10); + + return chr; +} + +/** + * @brief convert string to hex integer + * + * @param s A pointer string buffer + * @return hex integer + */ +t_u32 +a2hex(t_s8 *s) +{ + t_u32 val = 0; + + if (!strncasecmp("0x", s, 2)) { + s += 2; + } + + while (*s && isxdigit(*s)) { + val = (val << 4) + hexc2bin(*s++); + } + + return val; +} + +/* + * @brief convert String to integer + * + * @param value A pointer to string + * @return integer + */ +t_u32 +a2hex_or_atoi(t_s8 *value) +{ + if (value[0] == '0' && (value[1] == 'X' || value[1] == 'x')) { + return a2hex(value + 2); + } else if (isdigit(*value)) { + return atoi(value); + } else { + return *value; + } +} + +/** + * @brief convert string to hex + * + * @param ptr A pointer to data buffer + * @param chr A pointer to return integer + * @return A pointer to next data field + */ +t_s8 * +convert2hex(t_s8 *ptr, t_u8 *chr) +{ + t_u8 val; + + for (val = 0; *ptr && isxdigit(*ptr); ptr++) { + val = (val * 16) + hexval(*ptr); + } + + *chr = val; + + return ptr; +} + +/** + * @brief Check the Hex String + * @param s A pointer to the string + * @return MLAN_STATUS_SUCCESS --HexString, MLAN_STATUS_FAILURE --not HexString + */ +int +ishexstring(t_s8 *s) +{ + int ret = MLAN_STATUS_FAILURE; + t_s32 tmp; + + if (!strncasecmp("0x", s, 2)) { + s += 2; + } + while (*s) { + tmp = toupper(*s); + if (((tmp >= 'A') && (tmp <= 'F')) || + ((tmp >= '0') && (tmp <= '9'))) { + ret = MLAN_STATUS_SUCCESS; + } else { + ret = MLAN_STATUS_FAILURE; + break; + } + s++; + } + + return ret; +} + +/** + * @brief Convert String to Integer + * @param buf A pointer to the string + * @return Integer + */ +int +atoval(t_s8 *buf) +{ + if (!strncasecmp(buf, "0x", 2)) + return a2hex(buf + 2); + else if (!ishexstring(buf)) + return a2hex(buf); + else + return atoi(buf); +} + +/** + * @brief Prepare host-command buffer + * @param fp File handler + * @param cmd_name Command name + * @param buf A pointer to comand buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf) +{ + t_s8 line[256], cmdname[256], *pos, cmdcode[10]; + HostCmd_DS_GEN *hostcmd; + int ln = 0; + int cmdname_found = 0, cmdcode_found = 0; + + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + hostcmd = (HostCmd_DS_GEN *)buf; + hostcmd->command = 0xffff; + + snprintf(cmdname, sizeof(cmdname), "%s={", cmd_name); + cmdname_found = 0; + while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) { + if (strcmp(pos, cmdname) == 0) { + cmdname_found = 1; + snprintf(cmdcode, sizeof(cmdcode), "CmdCode="); + cmdcode_found = 0; + while ((pos = + mlan_config_get_line(fp, line, sizeof(line), + &ln))) { + if (strncmp(pos, cmdcode, strlen(cmdcode)) == 0) { + cmdcode_found = 1; + hostcmd->command = + a2hex_or_atoi(pos + + strlen(cmdcode)); + hostcmd->size = S_DS_GEN; + mlan_get_hostcmd_data(fp, &ln, + buf + + hostcmd->size, + &hostcmd->size); + break; + } + } + if (!cmdcode_found) { + fprintf(stderr, + "mlanutl: CmdCode not found in conf file\n"); + return MLAN_STATUS_FAILURE; + } + break; + } + } + + if (!cmdname_found) { + fprintf(stderr, + "mlanutl: cmdname '%s' is not found in conf file\n", + cmd_name); + return MLAN_STATUS_FAILURE; + } + + hostcmd->seq_num = 0; + hostcmd->result = 0; + hostcmd->command = cpu_to_le16(hostcmd->command); + hostcmd->size = cpu_to_le16(hostcmd->size); + return MLAN_STATUS_SUCCESS; +} + +/** Config data header length */ +#define CFG_DATA_HEADER_LEN 6 + +/** + * @brief Prepare cfg-data buffer + * @param argc number of arguments + * @param argv A pointer to arguments array + * @param fp File handler + * @param buf A pointer to comand buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_cfg_data_buffer(int argc, char *argv[], FILE * fp, t_u8 *buf) +{ + int ln = 0, type; + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_802_11_CFG_DATA *pcfg_data; + + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + hostcmd = (HostCmd_DS_GEN *)buf; + hostcmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); + pcfg_data = (HostCmd_DS_802_11_CFG_DATA *)(buf + S_DS_GEN); + pcfg_data->action = + (argc == 4) ? HostCmd_ACT_GEN_GET : HostCmd_ACT_GEN_SET; + type = atoi(argv[3]); + if ((type < 1) || (type > 2)) { + fprintf(stderr, "mlanutl: Invalid register type\n"); + return MLAN_STATUS_FAILURE; + } else { + pcfg_data->type = type; + } + if (argc == 5) { + ln = fparse_for_hex(fp, pcfg_data->data); + } + pcfg_data->data_len = ln; + hostcmd->size = + cpu_to_le16(pcfg_data->data_len + S_DS_GEN + + CFG_DATA_HEADER_LEN); + pcfg_data->data_len = cpu_to_le16(pcfg_data->data_len); + pcfg_data->type = cpu_to_le16(pcfg_data->type); + pcfg_data->action = cpu_to_le16(pcfg_data->action); + + hostcmd->seq_num = 0; + hostcmd->result = 0; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Process host_cmd response + * @param buf A pointer to the response buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_host_cmd_resp(t_u8 *buf) +{ + HostCmd_DS_GEN *hostcmd = (HostCmd_DS_GEN *)buf; + int ret = MLAN_STATUS_SUCCESS; + + hostcmd->command = le16_to_cpu(hostcmd->command); + hostcmd->size = le16_to_cpu(hostcmd->size); + hostcmd->seq_num = le16_to_cpu(hostcmd->seq_num); + hostcmd->result = le16_to_cpu(hostcmd->result); + + hostcmd->command &= ~HostCmd_RET_BIT; + if (!hostcmd->result) { + switch (hostcmd->command) { + case HostCmd_CMD_CFG_DATA: + { + HostCmd_DS_802_11_CFG_DATA *pstcfgData = + (HostCmd_DS_802_11_CFG_DATA *)(buf + + S_DS_GEN); + pstcfgData->data_len = + le16_to_cpu(pstcfgData->data_len); + pstcfgData->action = + le16_to_cpu(pstcfgData->action); + + if (pstcfgData->action == HostCmd_ACT_GEN_GET) { + hexdump("cfgdata", pstcfgData->data, + pstcfgData->data_len, ' '); + } + break; + } + case HostCmd_CMD_802_11_TPC_ADAPT_REQ: + { + mlan_ioctl_11h_tpc_resp *tpcIoctlResp = + (mlan_ioctl_11h_tpc_resp *)(buf + + S_DS_GEN); + if (tpcIoctlResp->status_code == 0) { + printf("tpcrequest: txPower(%d), linkMargin(%d), rssi(%d)\n", tpcIoctlResp->tx_power, tpcIoctlResp->link_margin, tpcIoctlResp->rssi); + } else { + printf("tpcrequest: failure, status = %d\n", tpcIoctlResp->status_code); + } + break; + } + case HostCmd_CMD_802_11_CRYPTO: + { + t_u16 alg = + le16_to_cpu((t_u16) + *(buf + S_DS_GEN + + sizeof(t_u16))); + if (alg != CIPHER_TEST_AES_CCM) { + HostCmd_DS_802_11_CRYPTO *cmd = + (HostCmd_DS_802_11_CRYPTO *)(buf + + + S_DS_GEN); + cmd->encdec = le16_to_cpu(cmd->encdec); + cmd->algorithm = + le16_to_cpu(cmd->algorithm); + cmd->key_IV_length = + le16_to_cpu(cmd->key_IV_length); + cmd->key_length = + le16_to_cpu(cmd->key_length); + cmd->data.header.type = + le16_to_cpu(cmd->data.header. + type); + cmd->data.header.len = + le16_to_cpu(cmd->data.header. + len); + + printf("crypto_result: encdec=%d algorithm=%d,KeyIVLen=%d," " KeyLen=%d,dataLen=%d\n", cmd->encdec, cmd->algorithm, cmd->key_IV_length, cmd->key_length, cmd->data.header.len); + hexdump("KeyIV", cmd->keyIV, + cmd->key_IV_length, ' '); + hexdump("Key", cmd->key, + cmd->key_length, ' '); + hexdump("Data", cmd->data.data, + cmd->data.header.len, ' '); + } else { + HostCmd_DS_802_11_CRYPTO_AES_CCM + *cmd_aes_ccm = + (HostCmd_DS_802_11_CRYPTO_AES_CCM + *)(buf + S_DS_GEN); + + cmd_aes_ccm->encdec + = + le16_to_cpu(cmd_aes_ccm-> + encdec); + cmd_aes_ccm->algorithm = + le16_to_cpu(cmd_aes_ccm-> + algorithm); + cmd_aes_ccm->key_length = + le16_to_cpu(cmd_aes_ccm-> + key_length); + cmd_aes_ccm->nonce_length = + le16_to_cpu(cmd_aes_ccm-> + nonce_length); + cmd_aes_ccm->AAD_length = + le16_to_cpu(cmd_aes_ccm-> + AAD_length); + cmd_aes_ccm->data.header.type = + le16_to_cpu(cmd_aes_ccm->data. + header.type); + cmd_aes_ccm->data.header.len = + le16_to_cpu(cmd_aes_ccm->data. + header.len); + + printf("crypto_result: encdec=%d algorithm=%d, KeyLen=%d," " NonceLen=%d,AADLen=%d,dataLen=%d\n", cmd_aes_ccm->encdec, cmd_aes_ccm->algorithm, cmd_aes_ccm->key_length, cmd_aes_ccm->nonce_length, cmd_aes_ccm->AAD_length, cmd_aes_ccm->data.header.len); + + hexdump("Key", cmd_aes_ccm->key, + cmd_aes_ccm->key_length, ' '); + hexdump("Nonce", cmd_aes_ccm->nonce, + cmd_aes_ccm->nonce_length, ' '); + hexdump("AAD", cmd_aes_ccm->AAD, + cmd_aes_ccm->AAD_length, ' '); + hexdump("Data", cmd_aes_ccm->data.data, + cmd_aes_ccm->data.header.len, + ' '); + } + break; + } + case HostCmd_CMD_802_11_AUTO_TX: + { + HostCmd_DS_802_11_AUTO_TX *at = + (HostCmd_DS_802_11_AUTO_TX *)(buf + + S_DS_GEN); + + if (le16_to_cpu(at->action) == + HostCmd_ACT_GEN_GET) { + if (S_DS_GEN + sizeof(at->action) == + hostcmd->size) { + printf("auto_tx not configured\n"); + + } else { + MrvlIEtypesHeader_t *header = + &at->auto_tx.header; + + header->type = + le16_to_cpu(header-> + type); + header->len = + le16_to_cpu(header-> + len); + + if ((S_DS_GEN + + sizeof(at->action) + + + sizeof(MrvlIEtypesHeader_t) + + header->len == + hostcmd->size) && + (header->type == + TLV_TYPE_AUTO_TX)) { + + AutoTx_MacFrame_t *atmf + = + &at->auto_tx. + auto_tx_mac_frame; + + printf("Interval: %d second(s)\n", le16_to_cpu(atmf->interval)); + printf("Priority: %#x\n", atmf->priority); + printf("Frame Length: %d\n", le16_to_cpu(atmf->frame_len)); + printf("Dest Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->dest_mac_addr[0], atmf->dest_mac_addr[1], atmf->dest_mac_addr[2], atmf->dest_mac_addr[3], atmf->dest_mac_addr[4], atmf->dest_mac_addr[5]); + printf("Src Mac Address: " "%02x:%02x:%02x:%02x:%02x:%02x\n", atmf->src_mac_addr[0], atmf->src_mac_addr[1], atmf->src_mac_addr[2], atmf->src_mac_addr[3], atmf->src_mac_addr[4], atmf->src_mac_addr[5]); + + hexdump("Frame Payload", + atmf->payload, + le16_to_cpu + (atmf-> + frame_len) + - + MLAN_MAC_ADDR_LENGTH + * 2, ' '); + } else { + printf("incorrect auto_tx command response\n"); + } + } + } + break; + } + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + { + HostCmd_DS_802_11_SUBSCRIBE_EVENT *se = + (HostCmd_DS_802_11_SUBSCRIBE_EVENT + *)(buf + S_DS_GEN); + if (le16_to_cpu(se->action) == + HostCmd_ACT_GEN_GET) { + int len = + S_DS_GEN + + sizeof + (HostCmd_DS_802_11_SUBSCRIBE_EVENT); + printf("\nEvent\t\tValue\tFreq\tsubscribed\n\n"); + while (len < hostcmd->size) { + MrvlIEtypesHeader_t *header = + (MrvlIEtypesHeader_t + *)(buf + len); + switch (le16_to_cpu + (header->type)) { + case TLV_TYPE_RSSI_LOW: + { + MrvlIEtypes_RssiThreshold_t + *low_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Beacon Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0001) ? "yes" : "no"); + break; + } + case TLV_TYPE_SNR_LOW: + { + MrvlIEtypes_SnrThreshold_t + *low_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Beacon Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0002) ? "yes" : "no"); + break; + } + case TLV_TYPE_FAILCOUNT: + { + MrvlIEtypes_FailureCount_t + *failure_count + = + (MrvlIEtypes_FailureCount_t + *)(buf + + + len); + printf("Failure Count\t%d\t%d\t%s\n", failure_count->fail_value, failure_count->fail_freq, (le16_to_cpu(se->events) & 0x0004) ? "yes" : "no"); + break; + } + case TLV_TYPE_BCNMISS: + { + MrvlIEtypes_BeaconsMissed_t + *bcn_missed + = + (MrvlIEtypes_BeaconsMissed_t + *)(buf + + + len); + printf("Beacon Missed\t%d\tN/A\t%s\n", bcn_missed->beacon_missed, (le16_to_cpu(se->events) & 0x0008) ? "yes" : "no"); + break; + } + case TLV_TYPE_RSSI_HIGH: + { + MrvlIEtypes_RssiThreshold_t + *high_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Bcn High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0010) ? "yes" : "no"); + break; + } + + case TLV_TYPE_SNR_HIGH: + { + MrvlIEtypes_SnrThreshold_t + *high_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Beacon High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0020) ? "yes" : "no"); + break; + } + case TLV_TYPE_RSSI_LOW_DATA: + { + MrvlIEtypes_RssiThreshold_t + *low_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Data Low RSSI\t%d\t%d\t%s\n", low_rssi->RSSI_value, low_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0040) ? "yes" : "no"); + break; + } + case TLV_TYPE_SNR_LOW_DATA: + { + MrvlIEtypes_SnrThreshold_t + *low_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Data Low SNR\t%d\t%d\t%s\n", low_snr->SNR_value, low_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0080) ? "yes" : "no"); + break; + } + case TLV_TYPE_RSSI_HIGH_DATA: + { + MrvlIEtypes_RssiThreshold_t + *high_rssi + = + (MrvlIEtypes_RssiThreshold_t + *)(buf + + + len); + printf("Data High RSSI\t%d\t%d\t%s\n", high_rssi->RSSI_value, high_rssi->RSSI_freq, (le16_to_cpu(se->events) & 0x0100) ? "yes" : "no"); + break; + } + case TLV_TYPE_SNR_HIGH_DATA: + { + MrvlIEtypes_SnrThreshold_t + *high_snr + = + (MrvlIEtypes_SnrThreshold_t + *)(buf + + + len); + printf("Data High SNR\t%d\t%d\t%s\n", high_snr->SNR_value, high_snr->SNR_freq, (le16_to_cpu(se->events) & 0x0200) ? "yes" : "no"); + break; + } + case TLV_TYPE_LINK_QUALITY: + { + MrvlIEtypes_LinkQuality_t + *link_qual + = + (MrvlIEtypes_LinkQuality_t + *)(buf + + + len); + printf("Link Quality Parameters:\n"); + printf("------------------------\n"); + printf("Link Quality Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0400) ? "yes" : "no"); + printf("Link SNR Threshold = %d\n", le16_to_cpu(link_qual->link_SNR_thrs)); + printf("Link SNR Frequency = %d\n", le16_to_cpu(link_qual->link_SNR_freq)); + printf("Min Rate Value = %d\n", le16_to_cpu(link_qual->min_rate_val)); + printf("Min Rate Frequency = %d\n", le16_to_cpu(link_qual->min_rate_freq)); + printf("Tx Latency Value = %d\n", le32_to_cpu(link_qual->tx_latency_val)); + printf("Tx Latency Threshold = %d\n", le32_to_cpu(link_qual->tx_latency_thrs)); + + break; + } + case TLV_TYPE_PRE_BEACON_LOST: + { + MrvlIEtypes_PreBeaconLost_t + *pre_bcn_lost + = + (MrvlIEtypes_PreBeaconLost_t + *)(buf + + + len); + printf("------------------------\n"); + printf("Pre-Beacon Lost Event Subscribed\t%s\n", (le16_to_cpu(se->events) & 0x0800) ? "yes" : "no"); + printf("Pre-Beacon Lost: %d\n", pre_bcn_lost->pre_beacon_lost); + break; + } + default: + printf("Unknown subscribed event TLV Type=%#x," " Len=%d\n", le16_to_cpu(header->type), le16_to_cpu(header->len)); + break; + } + + len += (sizeof + (MrvlIEtypesHeader_t) + + + le16_to_cpu(header-> + len)); + } + } + break; + } + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + { + HostCmd_DS_REG *preg = + (HostCmd_DS_REG *)(buf + S_DS_GEN); + preg->action = le16_to_cpu(preg->action); + if (preg->action == HostCmd_ACT_GEN_GET) { + preg->value = le32_to_cpu(preg->value); + printf("value = 0x%08x\n", preg->value); + } + break; + } + case HostCmd_CMD_MEM_ACCESS: + { + HostCmd_DS_MEM *pmem = + (HostCmd_DS_MEM *)(buf + S_DS_GEN); + pmem->action = le16_to_cpu(pmem->action); + if (pmem->action == HostCmd_ACT_GEN_GET) { + pmem->value = le32_to_cpu(pmem->value); + printf("value = 0x%08x\n", pmem->value); + } + break; + } + default: + printf("HOSTCMD_RESP: CmdCode=%#04x, Size=%#04x," + " SeqNum=%#04x, Result=%#04x\n", + hostcmd->command, hostcmd->size, + hostcmd->seq_num, hostcmd->result); + hexdump("payload", + (t_void *)(buf + S_DS_GEN), + hostcmd->size - S_DS_GEN, ' '); + break; + } + } else { + printf("HOSTCMD failed: CmdCode=%#04x, Size=%#04x," + " SeqNum=%#04x, Result=%#04x\n", + hostcmd->command, hostcmd->size, + hostcmd->seq_num, hostcmd->result); + } + return ret; +} + +/** + * @brief Prepare ARP filter buffer + * @param fp File handler + * @param buf A pointer to the buffer + * @param length A pointer to the length of buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_arp_filter_buffer(FILE * fp, t_u8 *buf, t_u16 *length) +{ + t_s8 line[256], *pos; + int ln = 0; + int ret = MLAN_STATUS_SUCCESS; + int arpfilter_found = 0; + + memset(buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + while ((pos = mlan_config_get_line(fp, line, sizeof(line), &ln))) { + if (strcmp(pos, "arpfilter={") == 0) { + arpfilter_found = 1; + mlan_get_hostcmd_data(fp, &ln, buf, length); + break; + } + } + if (!arpfilter_found) { + fprintf(stderr, "mlanutl: 'arpfilter' not found in conf file"); + ret = MLAN_STATUS_FAILURE; + } + return ret; +} + +/** + * @brief Prepare the hostcmd for register access + * @param type Register type + * @param offset Register offset + * @param value Pointer to value (NULL for read) + * @param buf Pointer to hostcmd buffer + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf) +{ + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_REG *preg; + + hostcmd = (HostCmd_DS_GEN *)buf; + switch (type) { + case 1: + hostcmd->command = cpu_to_le16(HostCmd_CMD_MAC_REG_ACCESS); + break; + case 2: + hostcmd->command = cpu_to_le16(HostCmd_CMD_BBP_REG_ACCESS); + break; + case 3: + hostcmd->command = cpu_to_le16(HostCmd_CMD_RF_REG_ACCESS); + break; + case 5: + hostcmd->command = cpu_to_le16(HostCmd_CMD_CAU_REG_ACCESS); + break; + default: + printf("Invalid register set specified\n"); + return -EINVAL; + } + preg = (HostCmd_DS_REG *)(buf + S_DS_GEN); + preg->action = (value) ? HostCmd_ACT_GEN_SET : HostCmd_ACT_GEN_GET; + preg->action = cpu_to_le16(preg->action); + preg->offset = cpu_to_le16((t_u16)offset); + if (value) + preg->value = cpu_to_le32(*value); + else + preg->value = 0; + hostcmd->size = cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_REG)); + hostcmd->seq_num = 0; + hostcmd->result = 0; + + return MLAN_STATUS_SUCCESS; +}
diff --git a/wlan_sd8897/mapp/mlanutl/mlanhostcmd.h b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.h new file mode 100644 index 0000000..47a4cf9 --- /dev/null +++ b/wlan_sd8897/mapp/mlanutl/mlanhostcmd.h
@@ -0,0 +1,119 @@ +/** @file mlanhostcmd.h + * + * @brief This file contains command structures for mlanutl application + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 11/26/2008: initial version +************************************************************************/ +#ifndef _MLANHOSTCMD_H_ +#define _MLANHOSTCMD_H_ + +/** Find number of elements */ +#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) + +/** Size of command buffer */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) + +/** Host Command ID : Memory access */ +#define HostCmd_CMD_MEM_ACCESS 0x0086 + +/** Pre-Authenticate - 11r only */ +#define HostCmd_CMD_802_11_AUTHENTICATE 0x0011 + +/** Read/Write Mac register */ +#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 +/** Read/Write BBP register */ +#define HostCmd_CMD_BBP_REG_ACCESS 0x001a +/** Read/Write RF register */ +#define HostCmd_CMD_RF_REG_ACCESS 0x001b +/** Get TX Power data */ +#define HostCmd_CMD_802_11_RF_TX_POWER 0x001e +/** Host Command ID : CAU register access */ +#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed + +/** Host Command ID : 802.11 BG scan configuration */ +#define HostCmd_CMD_802_11_BG_SCAN_CONFIG 0x006b +/** Host Command ID : Configuration data */ +#define HostCmd_CMD_CFG_DATA 0x008f +/** Host Command ID : 802.11 TPC adapt req */ +#define HostCmd_CMD_802_11_TPC_ADAPT_REQ 0x0060 +/** Host Command ID : 802.11 crypto */ +#define HostCmd_CMD_802_11_CRYPTO 0x0078 +/** Host Command ID : 802.11 auto Tx */ +#define HostCmd_CMD_802_11_AUTO_TX 0x0082 + +/** Host Command ID : 802.11 subscribe event */ +#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 + +/** Host Command ID : Channel TRPC config */ +#define HostCmd_CMD_CHAN_TRPC_CONFIG 0x00fb + +/** TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 +/** TLV type : Beacon RSSI low */ +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 0x04) /* 0x0104 */ +/** TLV type : Beacon SNR low */ +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 0x05) /* 0x0105 */ +/** TLV type : Fail count */ +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 0x06) /* 0x0106 */ +/** TLV type : BCN miss */ +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 0x07) /* 0x0107 */ +/** TLV type : Beacon RSSI high */ +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 0x16) /* 0x0116 */ +/** TLV type : Beacon SNR high */ +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 0x17) /* 0x0117 */ +/** TLV type : Auto Tx */ +#define TLV_TYPE_AUTO_TX (PROPRIETARY_TLV_BASE_ID + 0x18) /* 0x0118 */ +/** TLV type :Link Quality */ +#define TLV_TYPE_LINK_QUALITY (PROPRIETARY_TLV_BASE_ID + 0x24) /* 0x0124 */ +/** TLV type : Data RSSI low */ +#define TLV_TYPE_RSSI_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x26) /* 0x0126 */ +/** TLV type : Data SNR low */ +#define TLV_TYPE_SNR_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x27) /* 0x0127 */ +/** TLV type : Data RSSI high */ +#define TLV_TYPE_RSSI_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x28) /* 0x0128 */ +/** TLV type : Data SNR high */ +#define TLV_TYPE_SNR_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x29) /* 0x0129 */ +/** TLV type: Pre-Beacon Lost */ +#define TLV_TYPE_PRE_BEACON_LOST (PROPRIETARY_TLV_BASE_ID + 0x49) /* 0x0149 */ + +/** TLV type : Channel TRPC */ +#define TLV_TYPE_CHAN_TRPC (PROPRIETARY_TLV_BASE_ID + 0x89) /* 0x0189 */ + +/** mlan_ioctl_11h_tpc_resp */ +typedef struct { + int status_code; + /**< Firmware command result status code */ + int tx_power;/**< Reported TX Power from the TPC Report */ + int link_margin; + /**< Reported Link margin from the TPC Report */ + int rssi; /**< RSSI of the received TPC Report frame */ +} __ATTRIB_PACK__ mlan_ioctl_11h_tpc_resp; + +/* Define general hostcmd data structure */ + +/** Convert String to integer */ +t_u32 a2hex_or_atoi(char *value); +char *mlan_config_get_line(FILE * fp, char *str, t_s32 size, int *lineno); + +int prepare_host_cmd_buffer(FILE * fp, char *cmd_name, t_u8 *buf); +int prepare_hostcmd_regrdwr(t_u32 type, t_u32 offset, t_u32 *value, t_u8 *buf); + +#endif /* _MLANHOSTCMD_H_ */
diff --git a/wlan_sd8897/mapp/mlanutl/mlanoffload.c b/wlan_sd8897/mapp/mlanutl/mlanoffload.c new file mode 100644 index 0000000..ffb3880 --- /dev/null +++ b/wlan_sd8897/mapp/mlanutl/mlanoffload.c
@@ -0,0 +1,2519 @@ +/** @file mlanoffload.c + * + * @brief This files contains mlanutl offload command handling. + * + * Copyright (C) 2008-2017, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/************************************************************************ +Change log: + 08/11/2009: initial version +************************************************************************/ + +#include "mlanutl.h" +#include "mlanhostcmd.h" +#include "mlanoffload.h" + +/******************************************************** + Local Variables +********************************************************/ + +t_void hexdump(char *prompt, t_void *p, t_s32 len, t_s8 delim); + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Remove unwanted spaces, tabs from a line + * + * @param data A pointer to the starting of the line + * @return NA + */ +static void +profile_param_polish(char *data) +{ + t_u8 i, j, len = 0; + char *ptr; + ptr = strrchr(data, '\r'); + if (ptr == NULL) { + ptr = strrchr(data, '\n'); + if (ptr == NULL) { + return; + } + } + len = ptr - data; + for (i = 0; i < len; i++) { + if ((*(data + i) == ' ') || (*(data + i) == '\t')) { + for (j = i; j < len; j++) { + data[j] = data[j + 1]; + } + i--; + len--; + } + } +} + +static int +ascii_value(char letter) +{ + if (letter >= '0' && letter <= '9') + return letter - '0'; + if (letter >= 'a' && letter <= 'f') + return letter - 'a' + 10; + if (letter >= 'A' && letter <= 'F') + return letter - 'A' + 10; + return -1; +} + +static int +twodigit_ascii(const char *nibble) +{ + int a, b; + a = ascii_value(*nibble++); + if (a < 0) + return -1; + b = ascii_value(*nibble++); + if (b < 0) + return -1; + return (a << 4) | b; +} + +/** + * @brief Read a network block from the profile configuration file + * + * @param fp file pointer of the configuration file + * @param p_head profile head + * @return MLAN_STATUS_SUCCESS + */ +static int +profile_read_block(FILE * fp, profile_entry_t *p_head) +{ + char line[0x100]; + char *ptr, *eptr; + t_u8 key_cnt = 0; + t_u8 i, wep_len; + int byte; + int tmpIdx; + unsigned int mac[ETH_ALEN]; + + while (fgets(line, sizeof(line), fp)) { + /* call function to remove spaces, tabs */ + profile_param_polish(line); + + if (strstr(line, "}") != NULL) { + ptr = strstr(line, "}"); + /* end of network */ + break; + + } else if (line[0] == '#') { + /* comments go ahead */ + continue; + + } else if (strstr(line, "bssid=") != NULL) { + ptr = strstr(line, "bssid="); + ptr = ptr + strlen("bssid="); + sscanf(ptr, "%2x:%2x:%2x:%2x:%2x:%2x", + mac + 0, mac + 1, mac + 2, mac + 3, mac + 4, + mac + 5); + for (tmpIdx = 0; (unsigned int)tmpIdx < NELEMENTS(mac); + tmpIdx++) { + p_head->bssid[tmpIdx] = (t_u8)mac[tmpIdx]; + } + + } else if (strstr(line, "ssid=") != NULL) { + + ptr = strstr(line, "ssid="); + ptr = ptr + strlen("ssid="); + eptr = strrchr(ptr + 1, '"'); + + if ((*ptr != '"') || (strrchr(ptr + 1, '"') == NULL)) { + + fprintf(stderr, "ssid not within quotes\n"); + break; + } + + p_head->ssid_len = + MIN(IW_ESSID_MAX_SIZE, eptr - ptr - 1); + strncpy((char *)p_head->ssid, ptr + 1, + p_head->ssid_len); + p_head->ssid[p_head->ssid_len] = '\0'; + + } else if (strstr(line, "psk=") != NULL) { + ptr = strstr(line, "psk="); + ptr = ptr + strlen("psk="); + if (*ptr != '"') { + p_head->psk_config = 1; + strncpy((char *)p_head->psk, ptr, KEY_LEN); + } else { + eptr = strrchr(ptr + 1, '"'); + if (eptr == NULL) { + fprintf(stderr, + "passphrase not within quotes.\n"); + break; + } + p_head->passphrase_len = + MIN(PHRASE_LEN, eptr - ptr - 1); + strncpy((char *)p_head->passphrase, ptr + 1, + p_head->passphrase_len); + } + } else if (strstr(line, "wep_key") != NULL) { + ptr = strstr(line, "wep_key"); + ptr = ptr + strlen("wep_key"); + key_cnt = atoi(ptr); + ptr++; + if (*ptr != '=') { + fprintf(stderr, + "invalid wep_key, missing =.\n"); + break; + } + eptr = strrchr(ptr + 1, '\r'); + if (eptr == NULL) { + eptr = strrchr(ptr + 1, '\n'); + if (eptr == NULL) { + fprintf(stderr, + "missing EOL from the wep_key config\n"); + break; + } + } + ptr++; + if (*ptr == '"') { + eptr = strrchr(ptr + 1, '"'); + if (eptr == NULL) { + fprintf(stderr, + "wep key does not end with quote.\n"); + break; + } + *eptr = '\0'; + p_head->wep_key_len[key_cnt] = eptr - ptr - 1; + strncpy((char *)p_head->wep_key[key_cnt], + ptr + 1, p_head->wep_key_len[key_cnt]); + } else { + while (*eptr == '\r' || *eptr == '\n') + eptr--; + *(eptr + 1) = '\0'; + wep_len = strlen(ptr); + if (wep_len & 0x01) { + fprintf(stderr, + "incorrect wep key %s.\n", ptr); + break; + } + p_head->wep_key_len[key_cnt] = wep_len / 2; + for (i = 0; i < wep_len / 2; i++) { + byte = twodigit_ascii(ptr); + if (byte == -1) { + fprintf(stderr, + "incorrect wep key %s.\n", + ptr); + break; + } + *(p_head->wep_key[key_cnt] + i) = + (t_u8)byte; + ptr += 2; + } + } + } else if (strstr(line, "key_mgmt=") != NULL) { + ptr = strstr(line, "key_mgmt="); + ptr = ptr + strlen("key_mgmt="); + eptr = strstr(ptr, "WPA-EAP"); + if (eptr != NULL) { + p_head->key_mgmt |= + PROFILE_DB_KEY_MGMT_IEEE8021X; + } + eptr = strstr(ptr, "WPA-PSK"); + if (eptr != NULL) { + p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_PSK; + } + eptr = strstr(ptr, "FT-EAP"); + if (eptr != NULL) { + p_head->key_mgmt |= + PROFILE_DB_KEY_MGMT_FT_IEEE8021X; + } + eptr = strstr(ptr, "FT-PSK"); + if (eptr != NULL) { + p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_FT_PSK; + } + eptr = strstr(ptr, "WPA-EAP-SHA256"); + if (eptr != NULL) { + p_head->key_mgmt |= + PROFILE_DB_KEY_MGMT_SHA256_IEEE8021X; + } + eptr = strstr(ptr, "WPA-PSK-SHA256"); + if (eptr != NULL) { + p_head->key_mgmt |= + PROFILE_DB_KEY_MGMT_SHA256_PSK; + } + eptr = strstr(ptr, "CCKM"); + if (eptr != NULL) { + p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_CCKM; + } + eptr = strstr(ptr, "NONE"); + if (eptr != NULL) { + p_head->key_mgmt |= PROFILE_DB_KEY_MGMT_NONE; + } + } else if (strstr(line, "proto=") != NULL) { + ptr = strstr(line, "proto="); + ptr = ptr + strlen("proto="); + eptr = strstr(ptr, "WPA"); + if (eptr != NULL) { + p_head->protocol |= PROFILE_DB_PROTO_WPA; + } + + eptr = strstr(ptr, "RSN"); + if (eptr != NULL) { + p_head->protocol |= PROFILE_DB_PROTO_WPA2; + + } + } else if (strstr(line, "pairwise=") != NULL) { + ptr = strstr(line, "pairwise="); + ptr = ptr + strlen("pairwise="); + eptr = strstr(ptr, "CCMP"); + if (eptr != NULL) { + p_head->pairwise_cipher |= + PROFILE_DB_CIPHER_CCMP; + } + eptr = strstr(ptr, "TKIP"); + if (eptr != NULL) { + p_head->pairwise_cipher |= + PROFILE_DB_CIPHER_TKIP; + } + } else if (strstr(line, "groupwise=") != NULL) { + ptr = strstr(line, "groupwise="); + ptr = ptr + strlen("groupwise="); + eptr = strstr(ptr, "CCMP"); + if (eptr != NULL) { + p_head->groupwise_cipher |= + PROFILE_DB_CIPHER_CCMP; + } + eptr = strstr(ptr, "TKIP"); + if (eptr != NULL) { + p_head->groupwise_cipher |= + PROFILE_DB_CIPHER_TKIP; + } + } else if (strstr(line, "wep_tx_keyidx=") != NULL) { + ptr = strstr(line, "wep_tx_keyidx="); + ptr = ptr + strlen("wep_tx_keyidx="); + p_head->wep_key_idx = atoi(ptr); + } else if (strstr(line, "roaming=") != NULL) { + ptr = strstr(line, "roaming="); + ptr = ptr + strlen("roaming="); + p_head->roaming = atoi(ptr); + } else if (strstr(line, "ccx=") != NULL) { + ptr = strstr(line, "ccx="); + ptr = ptr + strlen("ccx="); + p_head->ccx = atoi(ptr); + } else if (strstr(line, "mode=") != NULL) { + ptr = strstr(line, "mode="); + ptr = ptr + strlen("mode="); + p_head->mode = atoi(ptr); + } + } + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Issue profile command to add new profile to FW + * + * @param filename Name of profile file + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static int +profile_read_download(char *filename) +{ + int ret = MLAN_STATUS_SUCCESS; + struct ifreq ifr; + t_u8 *buffer = NULL, *pos = NULL; + int i = 0; + t_u16 temp, tempc; + t_u32 cmd_len = 0, cmd_header_len; + struct eth_priv_cmd *cmd = NULL; + HostCmd_DS_GEN *hostcmd = NULL; + profile_entry_t *p_head = NULL; + FILE *fp; + char line[0x100]; + + fp = fopen(filename, "r"); + if (fp == NULL) { + perror("fopen"); + fprintf(stderr, "Cannot open file %s\n", filename); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD); + + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (buffer == NULL) { + fprintf(stderr, "Cannot alloc memory\n"); + ret = ENOMEM; + goto done; + } + memset(buffer, 0, BUFFER_LENGTH); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + free(buffer); + ret = ENOMEM; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = BUFFER_LENGTH; + + /* buffer = MRVL_CMD<cmd> */ + strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL)); + strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD)); + + /* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */ + hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32)); + + pos = (t_u8 *)hostcmd; + while (fgets(line, sizeof(line), fp)) { + /* call function to remove spaces, tabs */ + profile_param_polish(line); + if ((strstr(line, "network={") == NULL) || (line[0] == '#')) { + continue; + } + /* + * Memory allocation of every network block + */ + p_head = (profile_entry_t *)malloc(sizeof(profile_entry_t)); + if (p_head == NULL) { + fprintf(stderr, "Memory error.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + memset(p_head, 0x00, sizeof(profile_entry_t)); + + ret = profile_read_block(fp, p_head); + if (ret || p_head->ssid_len == 0) { + free(p_head); + continue; + } + + /* + * Put all the ssid parameters in the buffer + */ + memset(pos, 0, + (BUFFER_LENGTH - cmd_header_len - sizeof(t_u32))); + + /* Cmd Header : Command */ + hostcmd->command = cpu_to_le16(HostCmd_CMD_PROFILE_DB); + cmd_len = sizeof(HostCmd_DS_GEN); + + /* set action as set */ + tempc = cpu_to_le16(HostCmd_ACT_GEN_SET); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + + /* ssid */ + tempc = cpu_to_le16(TLV_TYPE_SSID); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = strlen((char *)p_head->ssid); + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + memcpy((void *)(pos + cmd_len), p_head->ssid, temp); + cmd_len += temp; + + if (memcmp(p_head->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN)) { + /* bssid */ + tempc = cpu_to_le16(TLV_TYPE_BSSID); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = ETH_ALEN; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + memcpy((void *)(pos + cmd_len), p_head->bssid, temp); + cmd_len += temp; + } + + /* proto */ + if (p_head->protocol == 0) { + p_head->protocol = 0xFFFF; + } + + tempc = cpu_to_le16(TLV_TYPE_PROTO); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = 2; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + memcpy((pos + cmd_len), &(p_head->protocol), temp); + cmd_len += temp; + + /* key_mgmt */ + if (p_head->key_mgmt == 0) { + p_head->key_mgmt = 0xFFFF; + } + + tempc = cpu_to_le16(TLV_TYPE_AKMP); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = 2; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + memcpy((pos + cmd_len), &(p_head->key_mgmt), temp); + cmd_len += temp; + + /* pairwise */ + if (p_head->pairwise_cipher == 0) { + p_head->pairwise_cipher = 0xFF; + } + + /* groupwise */ + if (p_head->groupwise_cipher == 0) { + p_head->groupwise_cipher = 0xFF; + } + + tempc = cpu_to_le16(TLV_TYPE_CIPHER); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = 2; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + pos[cmd_len] = p_head->pairwise_cipher; + cmd_len += 1; + pos[cmd_len] = p_head->groupwise_cipher; + cmd_len += 1; + + if (p_head->passphrase_len) { + /* passphrase */ + tempc = cpu_to_le16(TLV_TYPE_PASSPHRASE); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = p_head->passphrase_len; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + memcpy((void *)(pos + cmd_len), p_head->passphrase, + temp); + cmd_len += temp; + } + + if (p_head->psk_config) { + /* psk method */ + tempc = cpu_to_le16(TLV_TYPE_PMK); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = 32; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + memcpy((void *)(pos + cmd_len), p_head->psk, temp); + cmd_len += temp; + } + + for (i = 0; i < WEP_KEY_CNT; i++) { + if (p_head->wep_key_len[i]) { + /* TAG_WEP_KEY */ + tempc = cpu_to_le16(TLV_TYPE_WEP_KEY); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + /* wep_key_len + sizeof(keyIndex) + sizeof(IsDefault) */ + tempc = cpu_to_le16(p_head->wep_key_len[i] + 1 + + 1); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + *(pos + cmd_len) = i; + cmd_len += 1; + *(pos + cmd_len) = (i == p_head->wep_key_idx); + cmd_len += 1; + temp = p_head->wep_key_len[i]; + memcpy((void *)(pos + cmd_len), + p_head->wep_key[i], temp); + cmd_len += temp; + } + } + + if (p_head->roaming | p_head->ccx) { + tempc = cpu_to_le16(TLV_TYPE_OFFLOAD_ENABLE); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = 2; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + tempc = 0; + if (p_head->roaming) + tempc |= PROFILE_DB_FEATURE_ROAMING; + if (p_head->ccx) + tempc |= PROFILE_DB_FEATURE_CCX; + tempc = cpu_to_le16(tempc); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += temp; + } + + /* Put buffer length */ + memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32)); + hostcmd->size = cpu_to_le16(cmd_len); + + fprintf(stdout, "Downloading profile: %s ... ", p_head->ssid); + fflush(stdout); + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + /* Perform ioctl */ + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("ioctl[profiledb ioctl]"); + printf("ERR:Command sending failed!\n"); + ret = -EFAULT; + goto done; + } else { + hostcmd->result = le16_to_cpu(hostcmd->result); + if (hostcmd->result != 0) { + printf("hostcmd : profiledb ioctl failure, code %d\n", hostcmd->result); + ret = -EFAULT; + goto done; + } + } + + fprintf(stdout, "done.\n"); + + if (p_head) + free(p_head); + } + +done: + if (buffer) + free(buffer); + if (cmd) + free(cmd); + if (fp) + fclose(fp); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Process sub command + * + * @param sub_cmd Sub command + * @param num_sub_cmds Number of subcommands + * @param argc Number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_sub_cmd(sub_cmd_exec_t *sub_cmd, int num_sub_cmds, + int argc, char *argv[]) +{ + int idx; + boolean invalid_cmd = TRUE; + int ret = MLAN_STATUS_FAILURE; + + if (argv[3]) { + for (idx = 0; idx < num_sub_cmds; idx++) { + if (strncmp(argv[3], + sub_cmd[idx].str, + sub_cmd[idx].match_len) == 0) { + invalid_cmd = FALSE; + ret = sub_cmd[idx].callback(argc - 4, argv + 4); + break; + } + } + } + + if (invalid_cmd) { + printf("\nUnknown %s command. Valid subcmds:\n", argv[2]); + for (idx = 0; idx < num_sub_cmds; idx++) { + if (sub_cmd[idx].display) { + printf(" - %s\n", sub_cmd[idx].str); + } + } + printf("\n"); + } + + return ret; +} + +/** + * @brief select the table's regclass + * + * @param table_str Reg channel table type + * @param pTable Pointer to the Reg channel table + * + * @return TRUE if success otherwise FALSE + */ +boolean +reg_class_table_select(char *table_str, reg_chan_table_e *pTable) +{ + boolean retval = TRUE; + + if (strcmp(table_str, "user") == 0) { + *pTable = REGTABLE_USER; + } else if ((strcmp(table_str, "md") == 0) || + (strncmp(table_str, "multidomain", 5) == 0)) { + *pTable = REGTABLE_MULTIDOMAIN; + } else if (strcmp(table_str, "ess") == 0) { + *pTable = REGTABLE_ESS; + } else if (strcmp(table_str, "default") == 0) { + *pTable = REGTABLE_DEFAULT; + } else { /* If no option/wrong option set to default */ + *pTable = REGTABLE_DEFAULT; + } + + return retval; +} + +/** + * @brief Issue a measurement timing command + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_measurement(int argc, char *argv[]) +{ + int ret = 0; + struct ifreq ifr; + t_u8 *buffer = NULL, *pos = NULL; + t_u32 cmd_len = 0, cmd_header_len; + struct eth_priv_cmd *cmd = NULL; + HostCmd_DS_GEN *hostcmd = NULL; + HostCmd_DS_MEASUREMENT_Timing *timing_cmd = NULL; + MrvlIETypes_MeasTiming_t *timing_tlv = NULL; + int idx, rsp_len; + t_u8 sel = 0; + t_u16 tlv_len = 0; + timing_sel_t sel_str[] = { {"disconnected", 1}, + {"adhoc", 1}, + {"fullpower", 1}, + {"ieeeps", 1}, + {"periodic", 1} + }; + + if ((argc < 4) || strncmp(argv[3], "timing", + MAX(strlen("timing"), strlen(argv[3])))) { + printf("\nUnknown %s command. Valid subcmd: timing \n", + argv[2]); + return MLAN_STATUS_FAILURE; + } + + cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD); + cmd_len = S_DS_GEN + sizeof(t_u16); + + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (buffer == NULL) { + fprintf(stderr, "Cannot alloc memory\n"); + return -ENOMEM; + } + memset(buffer, 0, BUFFER_LENGTH); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + free(buffer); + return -ENOMEM; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = BUFFER_LENGTH; + + /* buffer = MRVL_CMD<cmd> */ + strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL)); + strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD)); + + /* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */ + hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32)); + hostcmd->command = cpu_to_le16(HostCmd_CMD_MEASUREMENT_TIMING_CONFIG); + hostcmd->size = cmd_len; + hostcmd->seq_num = 0; + hostcmd->result = 0; + + /* Point after host command header */ + pos = (t_u8 *)hostcmd + S_DS_GEN; + + timing_cmd = (HostCmd_DS_MEASUREMENT_Timing *)pos; + timing_cmd->action = cpu_to_le16(HostCmd_ACT_GEN_GET); + timing_tlv = (MrvlIETypes_MeasTiming_t *)timing_cmd->tlv_buffer; + + if (argc == 7) { + timing_cmd->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + timing_tlv->header.type = + cpu_to_le16(TLV_TYPE_MEASUREMENT_TIMING); + timing_tlv->header.len = + cpu_to_le16(sizeof(MrvlIETypes_MeasTiming_t) + - sizeof(timing_tlv->header)); + + for (idx = 1; (unsigned int)idx < NELEMENTS(sel_str); idx++) { + if (strncmp + (argv[4], sel_str[idx].str, + sel_str[idx].match_len) == 0) { + sel = idx + 1; + break; + } + } + + if (idx == NELEMENTS(sel_str)) { + printf("Wrong argument for mode selected \"%s\"\n", + argv[4]); + ret = -EINVAL; + goto done; + } + + timing_tlv->mode = cpu_to_le32(sel); + timing_tlv->max_off_channel = cpu_to_le32(atoi(argv[5])); + timing_tlv->max_on_channel = cpu_to_le32(atoi(argv[6])); + cmd_len += sizeof(MrvlIETypes_MeasTiming_t); + } + + /* Put buffer length */ + memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32)); + hostcmd->size = cpu_to_le16(cmd_len); + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + /* Perform ioctl */ + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("ioctl[measurement timing ioctl]"); + printf("ERR:Command sending failed!\n"); + ret = -EFAULT; + goto done; + } + + printf("--------------------------------------------------\n"); + printf("%44s\n", "Measurement Timing Profiles (in ms)"); + printf("--------------------------------------------------\n"); + printf(" Profile | MaxOffChannel | MaxOnChannel\n"); + printf("--------------------------------------------------\n"); + + /* Changed to TLV parsing */ + rsp_len = le16_to_cpu(hostcmd->size); + rsp_len -= (S_DS_GEN + sizeof(t_u16)); + pos = (t_u8 *)hostcmd + S_DS_GEN + sizeof(t_u16); + while ((unsigned int)rsp_len > sizeof(MrvlIEtypesHeader_t)) { + switch (le16_to_cpu(*(t_u16 *)(pos))) { + case TLV_TYPE_MEASUREMENT_TIMING: + timing_tlv = (MrvlIETypes_MeasTiming_t *)pos; + tlv_len = le16_to_cpu(timing_tlv->header.len); + printf("%15s | %14d | %13d\n", + sel_str[le32_to_cpu(timing_tlv->mode) - 1].str, + (int)le32_to_cpu(timing_tlv->max_off_channel), + (int)le32_to_cpu(timing_tlv->max_on_channel)); + break; + } + pos += tlv_len + sizeof(MrvlIEtypesHeader_t); + rsp_len -= tlv_len + sizeof(MrvlIEtypesHeader_t); + rsp_len = (rsp_len > 0) ? rsp_len : 0; + } + printf("\n"); + +done: + if (buffer) + free(buffer); + if (cmd) + free(cmd); + return ret; +} + +/** + * @brief Issue a profile command + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_profile_entry(int argc, char *argv[]) +{ + int ret = MLAN_STATUS_SUCCESS; + struct ifreq ifr; + unsigned int mac[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int idx; + t_u16 temp, tempc; + char *ssid = NULL; + t_u8 *buffer = NULL, *pos = NULL; + t_u32 cmd_len = 0, cmd_header_len; + struct eth_priv_cmd *cmd = NULL; + HostCmd_DS_GEN *hostcmd = NULL; + + cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD); + + if (argc < 4) { + fprintf(stderr, "Invalid number of argument!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (!strncmp(argv[3], "delete", sizeof("delete"))) { + if (argc > 4) { + if (strncmp(argv[4], "bssid=", strlen("bssid=")) == 0) { + /* "bssid" token string handler */ + sscanf(argv[4] + strlen("bssid="), + "%2x:%2x:%2x:%2x:%2x:%2x", mac + 0, + mac + 1, mac + 2, mac + 3, mac + 4, + mac + 5); + } else if (strncmp(argv[4], "ssid=", strlen("ssid=")) == + 0) { + /* "ssid" token string handler */ + ssid = argv[4] + strlen("ssid="); + } else { + printf("Error: missing required option for command (ssid, bssid)\n"); + ret = -ENOMEM; + goto done; + } + printf("Driver profile delete request\n"); + } else { + printf("Error: missing required option for command (ssid, bssid)\n"); + ret = -ENOMEM; + goto done; + } + } else if (!strncmp(argv[3], "flush", sizeof("flush"))) { + printf("Driver profile flush request\n"); + } else { + ret = profile_read_download(argv[3]); + goto done; + } + + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (buffer == NULL) { + fprintf(stderr, "Cannot alloc memory\n"); + ret = -ENOMEM; + goto done; + } + memset(buffer, 0, BUFFER_LENGTH); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = -ENOMEM; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = BUFFER_LENGTH; + + /* buffer = MRVL_CMD<cmd> */ + strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL)); + strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD)); + + /* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */ + hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32)); + hostcmd->command = cpu_to_le16(HostCmd_CMD_PROFILE_DB); + hostcmd->size = 0; + hostcmd->seq_num = 0; + hostcmd->result = 0; + + /* Point after host command header */ + pos = (t_u8 *)hostcmd; + cmd_len = S_DS_GEN; + + /* set action as del */ + tempc = cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + + /* ssid */ + if (ssid) { + printf("For ssid %s\n", ssid); + tempc = cpu_to_le16(TLV_TYPE_SSID); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = strlen((char *)ssid); + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + memcpy((void *)(pos + cmd_len), ssid, temp); + cmd_len += temp; + } else { + /* bssid */ + if (mac[0] != 0xFF) { + printf("For bssid %02x:%02x:%02x:%02x:%02x:%02x\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + } + tempc = cpu_to_le16(TLV_TYPE_BSSID); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + temp = ETH_ALEN; + tempc = cpu_to_le16(temp); + memcpy((pos + cmd_len), &tempc, sizeof(t_u16)); + cmd_len += 2; + for (idx = 0; (unsigned int)idx < NELEMENTS(mac); idx++) { + pos[cmd_len + idx] = (t_u8)mac[idx]; + } + cmd_len += temp; + } + + /* Put buffer length */ + memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32)); + hostcmd->size = cpu_to_le16(cmd_len); + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + /* Perform ioctl */ + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("ioctl[profiledb ioctl]"); + printf("ERR:Command sending failed!\n"); + ret = -EFAULT; + goto done; + } else { + hostcmd->result = le16_to_cpu(hostcmd->result); + if (hostcmd->result != 0) { + printf("hostcmd : profiledb ioctl failure, code %d\n", + hostcmd->result); + ret = -EFAULT; + goto done; + } + } + +done: + if (buffer) + free(buffer); + if (cmd) + free(cmd); + return ret; +} + +/** + * @brief Issue a chan report command + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_chanrpt(int argc, char *argv[]) +{ + int ret = MLAN_STATUS_SUCCESS; + int respLen; + struct ifreq ifr; + t_u8 *buffer = NULL, *pos = NULL; + t_u32 cmd_len = 0, cmd_header_len; + struct eth_priv_cmd *cmd = NULL; + t_u8 *pByte; + t_u8 numBins; + t_u8 idx; + MrvlIEtypes_Data_t *pTlvHdr; + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_CHAN_RPT_RSP *pChanRptRsp; + HostCmd_DS_CHAN_RPT_REQ *pChanRptReq; + + MrvlIEtypes_ChanRptBcn_t *pBcnRpt; + MrvlIEtypes_ChanRptChanLoad_t *pLoadRpt; + MrvlIEtypes_ChanRptNoiseHist_t *pNoiseRpt; + MrvlIEtypes_ChanRpt11hBasic_t *pBasicRpt; + MrvlIEtypes_ChanRptFrame_t *pFrameRpt; + + cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD); + + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (buffer == NULL) { + fprintf(stderr, "Cannot alloc memory\n"); + ret = ENOMEM; + goto done; + } + memset(buffer, 0, BUFFER_LENGTH); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = ENOMEM; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = BUFFER_LENGTH; + + /* buffer = MRVL_CMD<cmd> */ + strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL)); + strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD)); + + /* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */ + hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32)); + + /* Point after host command header */ + pos = (t_u8 *)hostcmd + S_DS_GEN; + + cmd_len = S_DS_GEN + sizeof(HostCmd_DS_CHAN_RPT_REQ); + + hostcmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); + hostcmd->size = cpu_to_le16(cmd_len); + hostcmd->seq_num = 0; + hostcmd->result = 0; + + pChanRptReq = (HostCmd_DS_CHAN_RPT_REQ *)pos; + pChanRptRsp = (HostCmd_DS_CHAN_RPT_RSP *)pos; + + memset((void *)pChanRptReq, 0x00, sizeof(HostCmd_DS_CHAN_RPT_REQ)); + + if ((argc != 5) && (argc != 6)) { + printf("\nchanrpt syntax: chanrpt <chan#> <millisecs> [sFreq]\n\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pChanRptReq->chanDesc.chanNum = atoi(argv[3]); + pChanRptReq->millisecDwellTime = cpu_to_le32(atoi(argv[4])); + + if (argc == 6) { + pChanRptReq->chanDesc.startFreq = cpu_to_le16(atoi(argv[5])); + } + + /* Put buffer length */ + memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32)); + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + /* Perform ioctl */ + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("ioctl[chanrpt hostcmd]"); + printf("ERR:Command sending failed!\n"); + ret = -EFAULT; + goto done; + } + + /* TSF is a t_u64, some formatted printing libs have + * trouble printing long longs, so cast and dump as bytes + */ + pByte = (t_u8 *)&pChanRptRsp->startTsf; + + printf("\n"); + printf("[%03d] TSF: 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", + atoi(argv[3]), + pByte[7], pByte[6], pByte[5], pByte[4], + pByte[3], pByte[2], pByte[1], pByte[0]); + printf("[%03d] Dwell: %u us\n", + atoi(argv[3]), (unsigned int)le32_to_cpu(pChanRptRsp->duration)); + + pByte = pChanRptRsp->tlvBuffer; + + respLen = le16_to_cpu(hostcmd->size) - sizeof(HostCmd_DS_GEN); + + respLen -= sizeof(pChanRptRsp->commandResult); + respLen -= sizeof(pChanRptRsp->startTsf); + respLen -= sizeof(pChanRptRsp->duration); + + pByte = pChanRptRsp->tlvBuffer; + + while ((unsigned int)respLen >= sizeof(pTlvHdr->header)) { + pTlvHdr = (MrvlIEtypes_Data_t *)pByte; + pTlvHdr->header.len = le16_to_cpu(pTlvHdr->header.len); + + switch (le16_to_cpu(pTlvHdr->header.type)) { + case TLV_TYPE_CHANRPT_BCN: + pBcnRpt = (MrvlIEtypes_ChanRptBcn_t *)pTlvHdr; + printf("[%03d] Beacon: scanReqId = %d\n", + atoi(argv[3]), pBcnRpt->scanReqId); + + break; + + case TLV_TYPE_CHANRPT_CHAN_LOAD: + pLoadRpt = (MrvlIEtypes_ChanRptChanLoad_t *)pTlvHdr; + printf("[%03d] ChanLoad: %d%%\n", + atoi(argv[3]), + (pLoadRpt->ccaBusyFraction * 100) / 255); + break; + + case TLV_TYPE_CHANRPT_NOISE_HIST: + pNoiseRpt = (MrvlIEtypes_ChanRptNoiseHist_t *)pTlvHdr; + numBins = + pNoiseRpt->header.len - sizeof(pNoiseRpt->anpi); + printf("[%03d] ANPI: %d dB\n", atoi(argv[3]), + le16_to_cpu(pNoiseRpt->anpi)); + printf("[%03d] NoiseHst:", atoi(argv[3])); + for (idx = 0; idx < numBins; idx++) { + printf(" %03d", pNoiseRpt->rpiDensities[idx]); + } + printf("\n"); + break; + + case TLV_TYPE_CHANRPT_11H_BASIC: + pBasicRpt = (MrvlIEtypes_ChanRpt11hBasic_t *)pTlvHdr; + printf("[%03d] 11hBasic: BSS(%d), OFDM(%d), UnId(%d), Radar(%d): " "[0x%02x]\n", atoi(argv[3]), pBasicRpt->map.BSS, pBasicRpt->map.OFDM_Preamble, pBasicRpt->map.Unidentified, pBasicRpt->map.Radar, *(t_u8 *)&pBasicRpt->map); + break; + + case TLV_TYPE_CHANRPT_FRAME: + pFrameRpt = (MrvlIEtypes_ChanRptFrame_t *)pTlvHdr; + printf("[%03d] Frame: %02x:%02x:%02x:%02x:%02x:%02x " "%02x:%02x:%02x:%02x:%02x:%02x %3d %02d\n", atoi(argv[3]), pFrameRpt->sourceAddr[0], pFrameRpt->sourceAddr[1], pFrameRpt->sourceAddr[2], pFrameRpt->sourceAddr[3], pFrameRpt->sourceAddr[4], pFrameRpt->sourceAddr[5], pFrameRpt->bssid[0], pFrameRpt->bssid[1], pFrameRpt->bssid[2], pFrameRpt->bssid[3], pFrameRpt->bssid[4], pFrameRpt->bssid[5], pFrameRpt->rssi, pFrameRpt->frameCnt); + break; + + default: + printf("[%03d] Other: Id=0x%x, Size = %d\n", + atoi(argv[3]), + pTlvHdr->header.type, pTlvHdr->header.len); + + break; + } + + pByte += (pTlvHdr->header.len + sizeof(pTlvHdr->header)); + respLen -= (pTlvHdr->header.len + sizeof(pTlvHdr->header)); + respLen = (respLen > 0) ? respLen : 0; + } + + printf("\n"); + +done: + if (buffer) + free(buffer); + if (cmd) + free(cmd); + return ret; +} + +/** + * @brief Issue a assoc timing command + * + * @param argc number of arguments + * @param argv A pointer to arguments array + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +int +process_assoc_timing(int argc, char *argv[]) +{ + int ret = MLAN_STATUS_SUCCESS; + struct ifreq ifr; + t_u8 *buffer = NULL, *pos = NULL; + t_u32 cmd_len = 0, cmd_header_len; + struct eth_priv_cmd *cmd = NULL; + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_AssociationTiming_t *assoctiming; + + cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD); + + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (buffer == NULL) { + fprintf(stderr, "Cannot alloc memory\n"); + ret = ENOMEM; + goto done; + } + memset(buffer, 0, BUFFER_LENGTH); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = ENOMEM; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = BUFFER_LENGTH; + + /* buffer = MRVL_CMD<cmd> */ + strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL)); + strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD)); + + /* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */ + hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32)); + + /* Point after host command header */ + pos = (t_u8 *)hostcmd + S_DS_GEN; + + cmd_len = S_DS_GEN + sizeof(HostCmd_DS_AssociationTiming_t); + + hostcmd->command = cpu_to_le16(HostCmd_CMD_ASSOCIATION_TIMING); + hostcmd->size = cpu_to_le16(cmd_len); + hostcmd->seq_num = 0; + hostcmd->result = 0; + + assoctiming = (HostCmd_DS_AssociationTiming_t *)pos; + assoctiming->Action = cpu_to_le16(HostCmd_ACT_GEN_GET); + + /* Put buffer length */ + memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32)); + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + /* Perform ioctl */ + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("ioctl[hostcmd]"); + printf("ERR:Command sending failed!\n"); + ret = -EFAULT; + goto done; + } + + if (argc > 3) { + assoctiming->Action = cpu_to_le16(HostCmd_ACT_GEN_SET); + switch (argc) { + case 9: + assoctiming->ReassocDiscMax = + cpu_to_le16(atoi(argv[8])); + /* No break, do everything below as well */ + case 8: + assoctiming->PriorApDeauthDelay = + cpu_to_le16(atoi(argv[7])); + /* No break, do everything below as well */ + case 7: + assoctiming->FrameExchangeTimeout = + cpu_to_le16(atoi(argv[6])); + /* No break, do everything below as well */ + case 6: + assoctiming->HandShakeTimeout = + cpu_to_le16(atoi(argv[5])); + /* No break, do everything below as well */ + case 5: + assoctiming->ReassocTimeout = + cpu_to_le16(atoi(argv[4])); + /* No break, do everything below as well */ + case 4: + assoctiming->AssocTimeout = cpu_to_le16(atoi(argv[3])); + break; + } + } + + /* Put buffer length */ + memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32)); + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + /* Perform ioctl */ + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("ioctl[hostcmd]"); + printf("ERR:Command sending failed!\n"); + ret = -EFAULT; + goto done; + } + + puts(""); + printf("------------------------------------------------\n"); + printf(" Association Timing Parameters\n"); + printf("------------------------------------------------\n"); + + printf("Association Timeout %5u ms\n" + "Reassociation Timeout %5u ms\n" + "Handshake Timeout %5u ms\n" + "Frame Exchange Timeout %5u ms\n" + "Prior AP Deauth Delay %5u ms\n" + "Reassoc Disconnect Max %5u ms\n", + le16_to_cpu(assoctiming->AssocTimeout), + le16_to_cpu(assoctiming->ReassocTimeout), + le16_to_cpu(assoctiming->HandShakeTimeout), + le16_to_cpu(assoctiming->FrameExchangeTimeout), + le16_to_cpu(assoctiming->PriorApDeauthDelay), + le16_to_cpu(assoctiming->ReassocDiscMax)); + puts(""); + +done: + if (buffer) + free(buffer); + if (cmd) + free(cmd); + return ret; +} + +/** + * @brief Retrieve the association response from the driver + * + * Retrieve the buffered (re)association management frame from the driver. + * The response is identical to the one received from the AP and conforms + * to the IEEE specification. + * + * @return MLAN_STATUS_SUCCESS or ioctl error code + */ +int +process_get_assocrsp(int argc, char *argv[]) +{ + int ret = 0; + t_u8 *buffer = NULL; + struct eth_priv_cmd *cmd = NULL; + struct ifreq ifr; + IEEEtypes_AssocRsp_t *pAssocRsp = NULL; + + /* Initialize buffer */ + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (!buffer) { + printf("ERR:Cannot allocate buffer for command!\n"); + return MLAN_STATUS_FAILURE; + } + + pAssocRsp = (IEEEtypes_AssocRsp_t *)buffer; + + /* buffer = MRVL_CMD<cmd> */ + strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL)); + strncpy((char *)buffer + strlen(CMD_MARVELL), argv[2], strlen(argv[2])); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + free(buffer); + return MLAN_STATUS_FAILURE; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = BUFFER_LENGTH; + + /* Perform IOCTL */ + memset(&ifr, 0, sizeof(struct ifreq)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("mlanutl"); + fprintf(stderr, "mlanutl: version fail\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (cmd->used_len) { + printf("getassocrsp: Status[%d], Cap[0x%04x]:\n", + pAssocRsp->StatusCode, + le16_to_cpu(*(t_u16 *)&pAssocRsp->Capability)); + hexdump(NULL, buffer, cmd->used_len, ' '); + } else { + printf("getassocrsp: <empty>\n"); + } + +done: + if (buffer) + free(buffer); + if (cmd) + free(cmd); + + return ret; +} + +/* +** Process mlanutl fcontrol command: +** +** mlanutl mlanX fcontrol %d [0xAA 0xBB... ] +** +** Sets and/or retrieves the feature control settings for a specific +** control set (argv[3] decimal argument). +** +*/ +int +process_fcontrol(int argc, char *argv[]) +{ + int ret = MLAN_STATUS_SUCCESS; + struct ifreq ifr; + t_u8 *buffer = NULL, *pos = NULL; + t_u32 cmd_len = 0, cmd_header_len; + struct eth_priv_cmd *cmd = NULL; + t_u8 idx; + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_OFFLOAD_FEATURE_CONTROL *pFcontrol; + + cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD); + + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (buffer == NULL) { + fprintf(stderr, "Cannot alloc memory\n"); + ret = ENOMEM; + goto done; + } + memset(buffer, 0, BUFFER_LENGTH); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = ENOMEM; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd)); + memcpy(&cmd->buf, &buffer, sizeof(buffer)); +#else + cmd->buf = buffer; +#endif + cmd->used_len = 0; + cmd->total_len = BUFFER_LENGTH; + + /* buffer = MRVL_CMD<cmd> */ + strncpy((char *)buffer, CMD_MARVELL, strlen(CMD_MARVELL)); + strncpy((char *)buffer + strlen(CMD_MARVELL), HOSTCMD, strlen(HOSTCMD)); + + /* buffer = MRVL_CMD<cmd><hostcmd_size><HostCmd_DS_GEN><CMD_DS> */ + hostcmd = (HostCmd_DS_GEN *)(buffer + cmd_header_len + sizeof(t_u32)); + + /* Point after host command header */ + pos = (t_u8 *)hostcmd + S_DS_GEN; + + pFcontrol = (HostCmd_DS_OFFLOAD_FEATURE_CONTROL *)pos; + + if (argc < 4) { + printf("Wrong number of arguments\n"); + ret = -EINVAL; + goto done; + } + + pFcontrol->controlSelect = atoi(argv[3]); + cmd_len = S_DS_GEN + sizeof(pFcontrol->controlSelect); + + for (idx = 4; idx < argc; idx++) { + pFcontrol->controlBitmap[idx - 4] = a2hex_or_atoi(argv[idx]); + cmd_len++; + } + + hostcmd->command = cpu_to_le16(HostCmd_CMD_OFFLOAD_FEATURE_CONTROL); + hostcmd->size = cpu_to_le16(cmd_len); + hostcmd->seq_num = 0; + hostcmd->result = 0; + + /* Put buffer length */ + memcpy(buffer + cmd_header_len, &cmd_len, sizeof(t_u32)); + + /* Initialize the ifr structure */ + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_ifrn.ifrn_name, dev_name, strlen(dev_name)); + ifr.ifr_ifru.ifru_data = (void *)cmd; + /* Perform ioctl */ + if (ioctl(sockfd, MLAN_ETH_PRIV, &ifr)) { + perror("ioctl[fcontrol hostcmd]"); + printf("ERR:Command sending failed!\n"); + ret = -EFAULT; + goto done; + } + + cmd_len = (le16_to_cpu(hostcmd->size) - sizeof(HostCmd_DS_GEN)); + + printf("Control[%d]", pFcontrol->controlSelect); + cmd_len--; + + for (idx = 0; idx < cmd_len; idx++) { + printf("\t0x%02x", pFcontrol->controlBitmap[idx]); + } + + printf("\n"); + +done: + if (buffer) + free(buffer); + if (cmd) + free(cmd); + return ret; +} + +/* +** Process mlanutl iapp command: +** +** mlanutl mlanX iapp <timeout> 0xAA 0xBB [0x... 0x.. ] +** +** 0xAA = IAPP type +** 0xBB = IAPP subtype +** 0x.. = Remaning bytes are iapp data +** +*/ +int +process_iapp(int argc, char *argv[]) +{ + int ret = MLAN_STATUS_SUCCESS; + struct ifreq ifr; + t_u8 *buffer = NULL, *pos = NULL; + t_u32 cmd_len = 0, cmd_header_len; + struct eth_priv_cmd *cmd = NULL; + t_u8 idx; + t_u8 fixlen; + HostCmd_DS_GEN *hostcmd; + HostCmd_DS_IAPP_PROXY *pIappProxy; + + cmd_header_len = strlen(CMD_MARVELL) + strlen(HOSTCMD); + + buffer = (t_u8 *)malloc(BUFFER_LENGTH); + if (buffer == NULL) { + fprintf(stderr, "Cannot alloc memory\n"); + ret = ENOMEM; + goto done; + } + memset(buffer, 0, BUFFER_LENGTH); + + cmd = (struct eth_priv_cmd *)malloc(sizeof(struct eth_priv_cmd)); + if (!cmd) { + printf("ERR:Cannot allocate buffer for command!\n"); + ret = ENOMEM; + goto done; + } + + /* Fill up buffer */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + memset(cmd, 0, sizeof(struct eth_priv_cmd))