This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "IPFire 2.x development tree".
The branch, master has been updated via dcd8c87594ba9e6a306a1cd62bf48362e2bfbb9f (commit) via 74fd06be0ec1cf8e2972e32461ae1580d1aafd5f (commit) from 7fa1b16e5d84108da9b218a28cb224961e523d35 (commit)
Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below.
- Log ----------------------------------------------------------------- commit dcd8c87594ba9e6a306a1cd62bf48362e2bfbb9f Merge: 74fd06b 7fa1b16 Author: Arne Fitzenreiter arne_f@ipfire.org Date: Thu May 8 16:15:13 2014 +0200
Merge branch 'master' of ssh://git.ipfire.org/pub/git/ipfire-2.x
commit 74fd06be0ec1cf8e2972e32461ae1580d1aafd5f Author: Arne Fitzenreiter arne_f@ipfire.org Date: Thu May 8 16:13:25 2014 +0200
kernel: add Marvel uAP module (dreamplug).
-----------------------------------------------------------------------
Summary of changes: .../kernel/kernel.config.armv5tel-ipfire-kirkwood | 1 + config/kernel/kernel.config.armv5tel-ipfire-multi | 1 + config/kernel/kernel.config.armv5tel-ipfire-rpi | 1 + config/kernel/kernel.config.i586-ipfire | 1 + config/kernel/kernel.config.i586-ipfire-pae | 1 + config/rootfiles/common/armv5tel/linux-kirkwood | 2 + config/rootfiles/common/armv5tel/linux-multi | 2 + config/rootfiles/common/armv5tel/linux-rpi | 2 + config/rootfiles/common/i586/linux | 2 + config/rootfiles/packages/linux-pae | 2 + lfs/linux | 1 + ....patch => linux-3.10.39-add_libertas_uap.patch} | 381 +++++++++++---------- 12 files changed, 207 insertions(+), 190 deletions(-) copy src/patches/{compat-wireless-3.5-libertas_uap.patch => linux-3.10.39-add_libertas_uap.patch} (94%)
Difference in files: diff --git a/config/kernel/kernel.config.armv5tel-ipfire-kirkwood b/config/kernel/kernel.config.armv5tel-ipfire-kirkwood index 5245a7f..2d9b115 100644 --- a/config/kernel/kernel.config.armv5tel-ipfire-kirkwood +++ b/config/kernel/kernel.config.armv5tel-ipfire-kirkwood @@ -1934,6 +1934,7 @@ CONFIG_WLAN=y CONFIG_LIBERTAS_THINFIRM=m # CONFIG_LIBERTAS_THINFIRM_DEBUG is not set CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_LIBERTAS_UAP=m CONFIG_ATMEL=m CONFIG_PCI_ATMEL=m CONFIG_AT76C50X_USB=m diff --git a/config/kernel/kernel.config.armv5tel-ipfire-multi b/config/kernel/kernel.config.armv5tel-ipfire-multi index dcd3b08..dcc177d 100644 --- a/config/kernel/kernel.config.armv5tel-ipfire-multi +++ b/config/kernel/kernel.config.armv5tel-ipfire-multi @@ -2141,6 +2141,7 @@ CONFIG_WLAN=y CONFIG_LIBERTAS_THINFIRM=m # CONFIG_LIBERTAS_THINFIRM_DEBUG is not set CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_LIBERTAS_UAP=m CONFIG_ATMEL=m CONFIG_PCI_ATMEL=m CONFIG_AT76C50X_USB=m diff --git a/config/kernel/kernel.config.armv5tel-ipfire-rpi b/config/kernel/kernel.config.armv5tel-ipfire-rpi index 84a06d9..0c9b3d2 100644 --- a/config/kernel/kernel.config.armv5tel-ipfire-rpi +++ b/config/kernel/kernel.config.armv5tel-ipfire-rpi @@ -1530,6 +1530,7 @@ CONFIG_WLAN=y CONFIG_LIBERTAS_THINFIRM=m # CONFIG_LIBERTAS_THINFIRM_DEBUG is not set CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_LIBERTAS_UAP=m CONFIG_AT76C50X_USB=m CONFIG_USB_ZD1201=m CONFIG_USB_NET_RNDIS_WLAN=m diff --git a/config/kernel/kernel.config.i586-ipfire b/config/kernel/kernel.config.i586-ipfire index 2824018..246e436 100644 --- a/config/kernel/kernel.config.i586-ipfire +++ b/config/kernel/kernel.config.i586-ipfire @@ -2120,6 +2120,7 @@ CONFIG_WLAN=y CONFIG_LIBERTAS_THINFIRM=m # CONFIG_LIBERTAS_THINFIRM_DEBUG is not set CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_LIBERTAS_UAP=m CONFIG_AIRO=m CONFIG_ATMEL=m CONFIG_PCI_ATMEL=m diff --git a/config/kernel/kernel.config.i586-ipfire-pae b/config/kernel/kernel.config.i586-ipfire-pae index d8f452c..8dc185b 100644 --- a/config/kernel/kernel.config.i586-ipfire-pae +++ b/config/kernel/kernel.config.i586-ipfire-pae @@ -2134,6 +2134,7 @@ CONFIG_WLAN=y CONFIG_LIBERTAS_THINFIRM=m # CONFIG_LIBERTAS_THINFIRM_DEBUG is not set CONFIG_LIBERTAS_THINFIRM_USB=m +CONFIG_LIBERTAS_UAP=m CONFIG_AIRO=m CONFIG_ATMEL=m CONFIG_PCI_ATMEL=m diff --git a/config/rootfiles/common/armv5tel/linux-kirkwood b/config/rootfiles/common/armv5tel/linux-kirkwood index cb6858f..9cb3057 100644 --- a/config/rootfiles/common/armv5tel/linux-kirkwood +++ b/config/rootfiles/common/armv5tel/linux-kirkwood @@ -1394,6 +1394,8 @@ lib/modules/KVER-ipfire-kirkwood #lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/libertas_tf #lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/libertas_tf/libertas_tf.ko #lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/libertas_tf/libertas_tf_usb.ko +#lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/libertas_uap +#lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/libertas_uap/uap8xxx.ko #lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/mac80211_hwsim.ko #lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/mwifiex #lib/modules/KVER-ipfire-kirkwood/kernel/drivers/net/wireless/mwifiex/mwifiex.ko diff --git a/config/rootfiles/common/armv5tel/linux-multi b/config/rootfiles/common/armv5tel/linux-multi index 89107a3..1fd840c 100644 --- a/config/rootfiles/common/armv5tel/linux-multi +++ b/config/rootfiles/common/armv5tel/linux-multi @@ -1356,6 +1356,8 @@ lib/modules/KVER-ipfire-multi #lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/libertas_tf #lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/libertas_tf/libertas_tf.ko #lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/libertas_tf/libertas_tf_usb.ko +#lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/libertas_uap +#lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/libertas_uap/uap8xxx.ko #lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/mac80211_hwsim.ko #lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/mwifiex #lib/modules/KVER-ipfire-multi/kernel/drivers/net/wireless/mwifiex/mwifiex.ko diff --git a/config/rootfiles/common/armv5tel/linux-rpi b/config/rootfiles/common/armv5tel/linux-rpi index 98f513e..2ea9efe 100644 --- a/config/rootfiles/common/armv5tel/linux-rpi +++ b/config/rootfiles/common/armv5tel/linux-rpi @@ -1125,6 +1125,8 @@ lib/modules/KVER-ipfire-rpi #lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/libertas_tf #lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/libertas_tf/libertas_tf.ko #lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/libertas_tf/libertas_tf_usb.ko +#lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/libertas_uap +#lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/libertas_uap/uap8xxx.ko #lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/mwifiex #lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/mwifiex/mwifiex.ko #lib/modules/KVER-ipfire-rpi/kernel/drivers/net/wireless/mwifiex/mwifiex_sdio.ko diff --git a/config/rootfiles/common/i586/linux b/config/rootfiles/common/i586/linux index 25eccdf..48bd9ce 100644 --- a/config/rootfiles/common/i586/linux +++ b/config/rootfiles/common/i586/linux @@ -1648,6 +1648,8 @@ lib/modules/KVER-ipfire #lib/modules/KVER-ipfire/kernel/drivers/net/wireless/libertas_tf #lib/modules/KVER-ipfire/kernel/drivers/net/wireless/libertas_tf/libertas_tf.ko #lib/modules/KVER-ipfire/kernel/drivers/net/wireless/libertas_tf/libertas_tf_usb.ko +#lib/modules/KVER-ipfire/kernel/drivers/net/wireless/libertas_uap +#lib/modules/KVER-ipfire/kernel/drivers/net/wireless/libertas_uap/uap8xxx.ko #lib/modules/KVER-ipfire/kernel/drivers/net/wireless/mac80211_hwsim.ko #lib/modules/KVER-ipfire/kernel/drivers/net/wireless/mwifiex #lib/modules/KVER-ipfire/kernel/drivers/net/wireless/mwifiex/mwifiex.ko diff --git a/config/rootfiles/packages/linux-pae b/config/rootfiles/packages/linux-pae index e46f855..eaaa3e5 100644 --- a/config/rootfiles/packages/linux-pae +++ b/config/rootfiles/packages/linux-pae @@ -1644,6 +1644,8 @@ lib/modules/KVER-ipfire-pae #lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/libertas_tf #lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/libertas_tf/libertas_tf.ko #lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/libertas_tf/libertas_tf_usb.ko +#lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/libertas_uap +#lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/libertas_uap/uap8xxx.ko #lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/mac80211_hwsim.ko #lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/mwifiex #lib/modules/KVER-ipfire-pae/kernel/drivers/net/wireless/mwifiex/mwifiex.ko diff --git a/lfs/linux b/lfs/linux index e6ac36c..2bf32d3 100644 --- a/lfs/linux +++ b/lfs/linux @@ -138,6 +138,7 @@ endif cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/compat-drivers-3.8.3-ath_ignore_eeprom_regdomain.patch cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.10.34-iwlwifi-noibss_only_on_radar_chan.patch cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.10.37-rt2800usb_add_dlink_dwa137_usbid.patch + cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/linux-3.10.39-add_libertas_uap.patch
# mISDN Patches cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/mISDN_hfc-s_add_id.patch diff --git a/src/patches/linux-3.10.39-add_libertas_uap.patch b/src/patches/linux-3.10.39-add_libertas_uap.patch new file mode 100644 index 0000000..be9a493 --- /dev/null +++ b/src/patches/linux-3.10.39-add_libertas_uap.patch @@ -0,0 +1,5081 @@ +diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig +index f8f0156..42a0702 100644 +--- a/drivers/net/wireless/Kconfig ++++ b/drivers/net/wireless/Kconfig +@@ -51,6 +51,14 @@ config LIBERTAS_THINFIRM_USB + ---help--- + A driver for Marvell Libertas 8388 USB devices using thinfirm. + ++config LIBERTAS_UAP ++ tristate "Marvell 8xxx Libertas UAP" ++ depends on MAC80211 ++ depends on MMC ++ select FW_LOADER ++ ---help--- ++ Driver for Marvell Libertas 8xxx micro AP. ++ + config AIRO + tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" + depends on ISA_DMA_API && (PCI || BROKEN) +diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile +index 67156ef..475a1b3 100644 +--- a/drivers/net/wireless/Makefile ++++ b/drivers/net/wireless/Makefile +@@ -37,6 +37,8 @@ obj-$(CONFIG_LIBERTAS) += libertas/ + + obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ + ++obj-$(CONFIG_LIBERTAS_UAP) += libertas_uap/ ++ + obj-$(CONFIG_ADM8211) += adm8211.o + + obj-$(CONFIG_MWL8K) += mwl8k.o +diff --git a/drivers/net/wireless/libertas_uap/Makefile b/drivers/net/wireless/libertas_uap/Makefile +new file mode 100644 +index 0000000..821f2a4 +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/Makefile +@@ -0,0 +1,6 @@ ++obj-$(CONFIG_LIBERTAS_UAP) += uap8xxx.o ++ ++uap8xxx-y += uap_main.o uap_sdio_mmc.o ++uap8xxx-$(CONFIG_PROC_FS) += uap_proc.o uap_debug.o ++ ++EXTRA_CFLAGS += -DFPNUM='"52"' -DPXA3XX_DMA_ALIGN -DDEBUG_LEVEL1 +diff --git a/drivers/net/wireless/libertas_uap/uap_debug.c b/drivers/net/wireless/libertas_uap/uap_debug.c +new file mode 100644 +index 0000000..a2f6dc9 +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_debug.c +@@ -0,0 +1,260 @@ ++/** @file uap_debug.c ++ * @brief This file contains functions for debug proc file. ++ * ++ * Copyright (C) 2008-2009, 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. ++ * ++ */ ++#ifdef CONFIG_PROC_FS ++#include "uap_headers.h" ++ ++/******************************************************** ++ Local Variables ++********************************************************/ ++ ++#define item_size(n) (sizeof ((uap_adapter *)0)->n) ++#define item_addr(n) ((u32) &((uap_adapter *)0)->n) ++ ++#define item_dbg_size(n) (sizeof (((uap_adapter *)0)->dbg.n)) ++#define item_dbg_addr(n) ((u32) &(((uap_adapter *)0)->dbg.n)) ++ ++#define item_dev_size(n) (sizeof ((uap_dev_t *)0)->n) ++#define item_dev_addr(n) ((u32) &((uap_dev_t *)0)->n) ++ ++/** MicroAp device offset */ ++#define OFFSET_UAP_DEV 0x01 ++/** Bluetooth adapter offset */ ++#define OFFSET_UAP_ADAPTER 0x02 ++ ++struct debug_data ++{ ++ /** Name */ ++ char name[32]; ++ /** Size */ ++ u32 size; ++ /** Address */ ++ u32 addr; ++ /** Offset */ ++ u32 offset; ++ /** Flag */ ++ u32 flag; ++}; ++ ++/* To debug any member of uap_adapter, simply add one line here. ++ */ ++static struct debug_data items[] = { ++ {"cmd_sent", item_dev_size(cmd_sent), 0, item_dev_addr(cmd_sent), ++ OFFSET_UAP_DEV}, ++ {"data_sent", item_dev_size(data_sent), 0, item_dev_addr(data_sent), ++ OFFSET_UAP_DEV}, ++ {"IntCounter", item_size(IntCounter), 0, item_addr(IntCounter), ++ OFFSET_UAP_ADAPTER}, ++ {"cmd_pending", item_size(cmd_pending), 0, item_addr(cmd_pending), ++ OFFSET_UAP_ADAPTER}, ++ {"num_cmd_h2c_fail", item_dbg_size(num_cmd_host_to_card_failure), 0, ++ item_dbg_addr(num_cmd_host_to_card_failure), OFFSET_UAP_ADAPTER}, ++ {"num_tx_h2c_fail", item_dbg_size(num_tx_host_to_card_failure), 0, ++ item_dbg_addr(num_tx_host_to_card_failure), OFFSET_UAP_ADAPTER}, ++ {"psmode", item_size(psmode), 0, item_addr(psmode), OFFSET_UAP_ADAPTER}, ++ {"ps_state", item_size(ps_state), 0, item_addr(ps_state), ++ OFFSET_UAP_ADAPTER}, ++#ifdef DEBUG_LEVEL1 ++ {"drvdbg", sizeof(drvdbg), (u32) & drvdbg, 0, 0} ++#endif ++}; ++ ++static int num_of_items = sizeof(items) / sizeof(items[0]); ++ ++/******************************************************** ++ Global Variables ++********************************************************/ ++ ++/******************************************************** ++ Local Functions ++********************************************************/ ++/** ++ * @brief proc read function ++ * ++ * @param page pointer to buffer ++ * @param s read data starting position ++ * @param off offset ++ * @param cnt counter ++ * @param eof end of file flag ++ * @param data data to output ++ * @return number of output data ++ */ ++static int uap_debug_proc_show(struct seq_file *s, void *data) { ++ int val = 0; ++ int i; ++ ++ struct debug_data *d = (struct debug_data *)s->private; ++ ++ if (MODULE_GET == 0) ++ return UAP_STATUS_FAILURE; ++ ++ for (i = 0; i < num_of_items; i++) { ++ if (d[i].size == 1) ++ val = *((u8 *) d[i].addr); ++ else if (d[i].size == 2) ++ val = *((u16 *) d[i].addr); ++ else if (d[i].size == 4) ++ val = *((u32 *) d[i].addr); ++ ++ seq_printf(s, "%s=%d\n", d[i].name, val); ++ } ++ MODULE_PUT; ++ return 0; ++} ++ ++static int uap_debug_proc_open(struct inode* inode, struct file* file) { ++ return single_open(file, uap_debug_proc_show, PDE_DATA(inode)); ++} ++ ++/** ++ * @brief proc write function ++ * ++ * @param f file pointer ++ * @param buf pointer to data buffer ++ * @param cnt data number to write ++ * @param data data to write ++ * @return number of data ++ */ ++static ssize_t uap_debug_proc_write(struct file *f, const char __user *buf, size_t cnt, loff_t *data) { ++ int r, i; ++ char *pdata; ++ char *p; ++ char *p0; ++ char *p1; ++ char *p2; ++ struct debug_data *d = (struct debug_data *)PDE_DATA(file_inode(f)); ++ ++ if (MODULE_GET == 0) ++ return UAP_STATUS_FAILURE; ++ ++ pdata = (char *) kmalloc(cnt, GFP_KERNEL); ++ if (pdata == NULL) { ++ MODULE_PUT; ++ return 0; ++ } ++ ++ if (copy_from_user(pdata, buf, cnt)) { ++ PRINTM(INFO, "Copy from user failed\n"); ++ kfree(pdata); ++ MODULE_PUT; ++ return 0; ++ } ++ ++ p0 = pdata; ++ for (i = 0; i < num_of_items; i++) { ++ do { ++ p = strstr(p0, d[i].name); ++ if (p == NULL) ++ break; ++ p1 = strchr(p, '\n'); ++ if (p1 == NULL) ++ break; ++ p0 = p1++; ++ p2 = strchr(p, '='); ++ if (!p2) ++ break; ++ p2++; ++ r = string_to_number(p2); ++ if (d[i].size == 1) ++ *((u8 *) d[i].addr) = (u8) r; ++ else if (d[i].size == 2) ++ *((u16 *) d[i].addr) = (u16) r; ++ else if (d[i].size == 4) ++ *((u32 *) d[i].addr) = (u32) r; ++ break; ++ } while (TRUE); ++ } ++ kfree(pdata); ++#ifdef DEBUG_LEVEL1 ++ printk(KERN_ALERT "drvdbg = 0x%x\n", drvdbg); ++ printk(KERN_ALERT "INFO (%08lx) %s\n", DBG_INFO, ++ (drvdbg & DBG_INFO) ? "X" : ""); ++ printk(KERN_ALERT "WARN (%08lx) %s\n", DBG_WARN, ++ (drvdbg & DBG_WARN) ? "X" : ""); ++ printk(KERN_ALERT "ENTRY (%08lx) %s\n", DBG_ENTRY, ++ (drvdbg & DBG_ENTRY) ? "X" : ""); ++ printk(KERN_ALERT "CMD_D (%08lx) %s\n", DBG_CMD_D, ++ (drvdbg & DBG_CMD_D) ? "X" : ""); ++ printk(KERN_ALERT "DAT_D (%08lx) %s\n", DBG_DAT_D, ++ (drvdbg & DBG_DAT_D) ? "X" : ""); ++ printk(KERN_ALERT "CMND (%08lx) %s\n", DBG_CMND, ++ (drvdbg & DBG_CMND) ? "X" : ""); ++ printk(KERN_ALERT "DATA (%08lx) %s\n", DBG_DATA, ++ (drvdbg & DBG_DATA) ? "X" : ""); ++ printk(KERN_ALERT "ERROR (%08lx) %s\n", DBG_ERROR, ++ (drvdbg & DBG_ERROR) ? "X" : ""); ++ printk(KERN_ALERT "FATAL (%08lx) %s\n", DBG_FATAL, ++ (drvdbg & DBG_FATAL) ? "X" : ""); ++ printk(KERN_ALERT "MSG (%08lx) %s\n", DBG_MSG, ++ (drvdbg & DBG_MSG) ? "X" : ""); ++#endif ++ MODULE_PUT; ++ return cnt; ++} ++ ++static const struct file_operations uap_debug_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = uap_debug_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .write = uap_debug_proc_write, ++}; ++ ++/******************************************************** ++ Global Functions ++********************************************************/ ++/** ++ * @brief create debug proc file ++ * ++ * @param priv pointer uap_private ++ * @param dev pointer net_device ++ * @return N/A ++ */ ++void ++uap_debug_entry(uap_private * priv, struct net_device *dev) ++{ ++ int i; ++ ++ if (priv->proc_entry == NULL) ++ return; ++ ++ for (i = 0; i < num_of_items; i++) { ++ if (items[i].flag & OFFSET_UAP_ADAPTER) ++ items[i].addr = items[i].offset + (u32) priv->adapter; ++ if (items[i].flag & OFFSET_UAP_DEV) ++ items[i].addr = items[i].offset + (u32) & priv->uap_dev; ++ } ++ proc_create_data("debug", 0644, priv->proc_entry, &uap_debug_proc_fops, ++ &items[0]); ++} ++ ++/** ++ * @brief remove proc file ++ * ++ * @param priv pointer uap_private ++ * @return N/A ++ */ ++void ++uap_debug_remove(uap_private * priv) ++{ ++ remove_proc_entry("debug", priv->proc_entry); ++} ++ ++#endif +diff --git a/drivers/net/wireless/libertas_uap/uap_drv.h b/drivers/net/wireless/libertas_uap/uap_drv.h +new file mode 100644 +index 0000000..5aa009f +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_drv.h +@@ -0,0 +1,667 @@ ++/** @file uap_drv.h ++ * @brief This file contains Linux OS related definitions and ++ * declarations, uAP driver ++ * ++ * Copyright (C) 2008-2009, 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 _UAP_DRV_H ++#define _UAP_DRV_H ++ ++/** Driver release version */ ++#define DRIVER_VERSION "26146" ++ ++/** True */ ++#ifndef TRUE ++#define TRUE 1 ++#endif ++/** False */ ++#ifndef FALSE ++#define FALSE 0 ++#endif ++ ++/** Bit definitions */ ++#ifndef BIT ++#define BIT(x) (1UL << (x)) ++#endif ++ ++/** Dma addresses are 32-bits wide. */ ++#ifndef __ATTRIB_ALIGN__ ++#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) ++#endif ++ ++/** attribute pack */ ++#ifndef __ATTRIB_PACK__ ++#define __ATTRIB_PACK__ __attribute__ ((packed)) ++#endif ++ ++/** Debug Macro definition*/ ++#ifdef DEBUG_LEVEL1 ++ ++extern u32 drvdbg; ++ ++/** Debug message control bit definition for drvdbg */ ++/** Debug message */ ++#define DBG_MSG BIT(0) ++/** Debug fatal message */ ++#define DBG_FATAL BIT(1) ++/** Debug error message */ ++#define DBG_ERROR BIT(2) ++/** Debug data message */ ++#define DBG_DATA BIT(3) ++/** Debug command message */ ++#define DBG_CMND BIT(4) ++ ++/** Debug data */ ++#define DBG_DAT_D BIT(16) ++/** Debug command */ ++#define DBG_CMD_D BIT(17) ++ ++/** Debug entry */ ++#define DBG_ENTRY BIT(28) ++/** Debug warning */ ++#define DBG_WARN BIT(29) ++/** Debug info */ ++#define DBG_INFO BIT(30) ++ ++/** Print info */ ++#define PRINTM_INFO(msg...) {if (drvdbg & DBG_INFO) printk(KERN_DEBUG msg);} ++/** Print warn message */ ++#define PRINTM_WARN(msg...) {if (drvdbg & DBG_WARN) printk(KERN_DEBUG msg);} ++/** Print entry */ ++#define PRINTM_ENTRY(msg...) {if (drvdbg & DBG_ENTRY) printk(KERN_DEBUG msg);} ++/** Print cmd_d */ ++#define PRINTM_CMD_D(msg...) {if (drvdbg & DBG_CMD_D) printk(KERN_DEBUG msg);} ++/** Print data_d */ ++#define PRINTM_DAT_D(msg...) {if (drvdbg & DBG_DAT_D) printk(KERN_DEBUG msg);} ++/** Print command */ ++#define PRINTM_CMND(msg...) {if (drvdbg & DBG_CMND) printk(KERN_DEBUG msg);} ++/** Print data */ ++#define PRINTM_DATA(msg...) {if (drvdbg & DBG_DATA) printk(KERN_DEBUG msg);} ++/** Print error message */ ++#define PRINTM_ERROR(msg...) {if (drvdbg & DBG_ERROR) printk(KERN_DEBUG msg);} ++/** Print fatal message */ ++#define PRINTM_FATAL(msg...) {if (drvdbg & DBG_FATAL) printk(KERN_DEBUG msg);} ++/** Print message */ ++#define PRINTM_MSG(msg...) {if (drvdbg & DBG_MSG) printk(KERN_ALERT msg);} ++/** Print level */ ++#define PRINTM(level,msg...) PRINTM_##level(msg) ++ ++#else ++ ++#define PRINTM(level,msg...) do {} while (0) ++ ++#endif /* DEBUG_LEVEL1 */ ++ ++/** Wait until a condition becomes true */ ++#define ASSERT(cond) \ ++do { \ ++ if (!(cond)) \ ++ PRINTM(INFO, "ASSERT: %s, %s:%i\n", \ ++ __FUNCTION__, __FILE__, __LINE__); \ ++} while(0) ++ ++/** Log enrty point for debugging */ ++#define ENTER() PRINTM(ENTRY, "Enter: %s, %s:%i\n", __FUNCTION__, \ ++ __FILE__, __LINE__) ++/** Log exit point for debugging */ ++#define LEAVE() PRINTM(ENTRY, "Leave: %s, %s:%i\n", __FUNCTION__, \ ++ __FILE__, __LINE__) ++ ++#ifdef DEBUG_LEVEL1 ++/** Dump buffer length */ ++#define DBG_DUMP_BUF_LEN 64 ++/** Maximum dump per line */ ++#define MAX_DUMP_PER_LINE 16 ++/** Data dump length */ ++#define DATA_DUMP_LEN 32 ++ ++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:\n", prompt); ++ for (i = 1; i <= len; i++) { ++ ptr += sprintf(ptr, "%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 command */ ++#define DBG_HEXDUMP_CMD_D(x,y,z) {if (drvdbg & DBG_CMD_D) hexdump(x,y,z);} ++/** Debug data */ ++#define DBG_HEXDUMP_DAT_D(x,y,z) {if (drvdbg & DBG_DAT_D) hexdump(x,y,z);} ++/** Debug hexdump */ ++#define DBG_HEXDUMP(level,x,y,z) DBG_HEXDUMP_##level(x,y,z) ++/** hexdump */ ++#define HEXDUMP(x,y,z) {if (drvdbg & DBG_INFO) hexdump(x,y,z);} ++#else ++/** Do nothing since debugging is not turned on */ ++#define DBG_HEXDUMP(level,x,y,z) do {} while (0) ++/** Do nothing since debugging is not turned on */ ++#define HEXDUMP(x,y,z) do {} while (0) ++#endif ++ ++/** ++ * Typedefs ++ */ ++/** Unsigned char */ ++typedef u8 BOOLEAN; ++ ++/* ++ * OS macro definitions ++ */ ++/** OS macro to get time */ ++#define os_time_get() jiffies ++ ++/** OS macro to update transfer start time */ ++#define UpdateTransStart(dev) { \ ++ dev->trans_start = jiffies; \ ++} ++ ++/** Try to get a reference to the module */ ++#define MODULE_GET try_module_get(THIS_MODULE) ++/** Decrease module reference count */ ++#define MODULE_PUT module_put(THIS_MODULE) ++ ++/** OS macro to initialize semaphore */ ++#define OS_INIT_SEMAPHORE(x) sema_init(x,1) ++/** OS macro to acquire blocking semaphore */ ++#define OS_ACQ_SEMAPHORE_BLOCK(x) down_interruptible(x) ++/** OS macro to acquire non-blocking semaphore */ ++#define OS_ACQ_SEMAPHORE_NOBLOCK(x) down_trylock(x) ++/** OS macro to release semaphore */ ++#define OS_REL_SEMAPHORE(x) up(x) ++ ++static inline void ++os_sched_timeout(u32 millisec) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout((millisec * HZ) / 1000); ++} ++ ++/** Maximum size of ethernet packet */ ++#define MRVDRV_MAXIMUM_ETH_PACKET_SIZE 1514 ++ ++/** Maximum size of multicast list */ ++#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 ++ ++/** Find minimum */ ++#ifndef MIN ++#define MIN(a,b) ((a) < (b) ? (a) : (b)) ++#endif ++ ++/** Find maximum */ ++#ifndef MAX ++#define MAX(a,b) ((a) > (b) ? (a) : (b)) ++#endif ++ ++/** Find number of elements */ ++#ifndef NELEMENTS ++#define NELEMENTS(x) (sizeof(x)/sizeof(x[0])) ++#endif ++ ++/** Buffer Constants */ ++ ++/** Size of command buffer */ ++#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) ++ ++/** Length of device length */ ++#define DEV_NAME_LEN 32 ++ ++/** Length of ethernet address */ ++#ifndef ETH_ALEN ++#define ETH_ALEN 6 ++#endif ++ ++/** Default watchdog timeout */ ++#define MRVDRV_DEFAULT_WATCHDOG_TIMEOUT (2 * HZ) ++ ++/** Success */ ++#define UAP_STATUS_SUCCESS (0) ++/** Failure */ ++#define UAP_STATUS_FAILURE (-1) ++/** Not accepted */ ++#define UAP_STATUS_NOT_ACCEPTED (-2) ++ ++/** Max loop count (* 100ms) for waiting device ready at init time */ ++#define MAX_WAIT_DEVICE_READY_COUNT 50 ++ ++/** Tx high watermark. Stop Tx queue after this is crossed */ ++#define TX_HIGH_WATERMARK 4 ++/** Tx low watermark. Restart Tx queue after this is crossed */ ++#define TX_LOW_WATERMARK 2 ++ ++/** 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 ++ ++/** 20 seconds */ ++#define MRVDRV_TIMER_20S 20000 ++ ++/** Host Command option for wait till Send */ ++#define HostCmd_OPTION_WAITFORSEND 0x0001 ++/** Host Command option for wait for RSP */ ++#define HostCmd_OPTION_WAITFORRSP 0x0002 ++/** Host Command option for wait for RSP or Timeout */ ++#define HostCmd_OPTION_WAITFORRSP_TIMEOUT 0x0003 ++/** Host Command option for wait for RSP of sleep confirm */ ++#define HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM 0x0004 ++ ++/** Sleep until a condition gets true or a timeout elapses */ ++#define os_wait_interruptible_timeout(waitq, cond, timeout) \ ++ wait_event_interruptible_timeout(waitq, cond, ((timeout) * HZ / 1000)) ++ ++/** Private command ID to Host command */ ++#define UAPHOSTCMD (SIOCDEVPRIVATE + 1) ++ ++/** Private command ID to Power Mode */ ++#define UAP_POWER_MODE (SIOCDEVPRIVATE + 3) ++/** sleep_param */ ++typedef struct _ps_sleep_param ++{ ++ /** control bitmap */ ++ u32 ctrl_bitmap; ++ /** minimum sleep period (micro second) */ ++ u32 min_sleep; ++ /** maximum sleep period (micro second) */ ++ u32 max_sleep; ++} ps_sleep_param; ++ ++/** inactivity sleep_param */ ++typedef struct _inact_sleep_param ++{ ++ /** inactivity timeout (micro second) */ ++ u32 inactivity_to; ++ /** miniumu awake period (micro second) */ ++ u32 min_awake; ++ /** maximum awake period (micro second) */ ++ u32 max_awake; ++} inact_sleep_param; ++ ++/** flag for ps mode */ ++#define PS_FLAG_PS_MODE 1 ++/** flag for sleep param */ ++#define PS_FLAG_SLEEP_PARAM 2 ++/** flag for inactivity sleep param */ ++#define PS_FLAG_INACT_SLEEP_PARAM 4 ++ ++/** Disable power mode */ ++#define PS_MODE_DISABLE 0 ++/** Enable periodic dtim ps */ ++#define PS_MODE_PERIODIC_DTIM 1 ++/** Enable inactivity ps */ ++#define PS_MODE_INACTIVITY 2 ++ ++/** sleep parameter */ ++#define SLEEP_PARAMETER 1 ++/** inactivity sleep parameter */ ++#define INACTIVITY_SLEEP_PARAMETER 2 ++/** ps_mgmt */ ++typedef struct _ps_mgmt ++{ ++ /** flags for valid field */ ++ u16 flags; ++ /** power mode */ ++ u16 ps_mode; ++ /** sleep param */ ++ ps_sleep_param sleep_param; ++ /** inactivity sleep param */ ++ inact_sleep_param inact_param; ++} ps_mgmt; ++ ++/** Semaphore structure */ ++typedef struct semaphore SEMAPHORE; ++ ++/** Global Varibale Declaration */ ++/** Private data structure of the device */ ++typedef struct _uap_private uap_private; ++/** Adapter data structure of the device */ ++typedef struct _uap_adapter uap_adapter; ++/** private structure */ ++extern uap_private *uappriv; ++ ++/** ENUM definition*/ ++ ++/** Hardware status codes */ ++typedef enum _HARDWARE_STATUS ++{ ++ HWReady, ++ HWInitializing, ++ HWReset, ++ HWClosing, ++ HWNotReady ++} HARDWARE_STATUS; ++ ++/** info for debug purpose */ ++typedef struct _uap_dbg ++{ ++ /** Number of host to card command failures */ ++ u32 num_cmd_host_to_card_failure; ++ /** Number of host to card Tx failures */ ++ u32 num_tx_host_to_card_failure; ++} uap_dbg; ++ ++/** Set thread state */ ++#define OS_SET_THREAD_STATE(x) set_current_state(x) ++ ++typedef struct ++{ ++ /** Task */ ++ struct task_struct *task; ++ /** Queue */ ++ wait_queue_head_t waitQ; ++ /** PID */ ++ pid_t pid; ++ /** Private structure */ ++ void *priv; ++} uap_thread; ++ ++static inline void ++uap_activate_thread(uap_thread * thr) ++{ ++ /** Record the thread pid */ ++ thr->pid = current->pid; ++ ++ /** Initialize the wait queue */ ++ init_waitqueue_head(&thr->waitQ); ++} ++ ++static inline void ++uap_deactivate_thread(uap_thread * thr) ++{ ++ thr->pid = 0; ++ return; ++} ++ ++static inline void ++uap_create_thread(int (*uapfunc) (void *), uap_thread * thr, char *name) ++{ ++ thr->task = kthread_run(uapfunc, thr, "%s", name); ++} ++ ++static inline int ++uap_terminate_thread(uap_thread * thr) ++{ ++ /* Check if the thread is active or not */ ++ if (!thr->pid) ++ return -1; ++ kthread_stop(thr->task); ++ return 0; ++} ++ ++/** Data structure for the Marvell uAP device */ ++typedef struct _uap_dev ++{ ++ /** device name */ ++ char name[DEV_NAME_LEN]; ++ /** card pointer */ ++ void *card; ++ /** IO port */ ++ u32 ioport; ++ /** Rx unit */ ++ u8 rx_unit; ++ /** Data sent: ++ TRUE - Data is sent to fw, no Tx Done received ++ FALSE - Tx done received for previous Tx */ ++ BOOLEAN data_sent; ++ /** CMD sent: ++ TRUE - CMD is sent to fw, no CMD Done received ++ FALSE - CMD done received for previous CMD */ ++ BOOLEAN cmd_sent; ++ /** netdev pointer */ ++ struct net_device *netdev; ++} uap_dev_t, *puap_dev_t; ++ ++/** Private structure for the MV device */ ++struct _uap_private ++{ ++ /** Device open */ ++ int open; ++ ++ /** Device adapter structure */ ++ uap_adapter *adapter; ++ /** Device structure */ ++ uap_dev_t uap_dev; ++ ++ /** Net device statistics structure */ ++ struct net_device_stats stats; ++ ++ /** Number of Tx timeouts */ ++ u32 num_tx_timeout; ++ ++ /** Media connection status */ ++ BOOLEAN MediaConnected; ++ ++#ifdef CONFIG_PROC_FS ++ struct proc_dir_entry *proc_uap; ++ struct proc_dir_entry *proc_entry; ++#endif /* CONFIG_PROC_FS */ ++ ++ /** Firmware helper */ ++ const struct firmware *fw_helper; ++ /** Firmware */ ++ const struct firmware *firmware; ++ /** Hotplug device */ ++ struct device *hotplug_device; ++ /** thread to service interrupts */ ++ uap_thread MainThread; ++ /** Driver lock */ ++ spinlock_t driver_lock; ++ /** Driver lock flags */ ++ ulong driver_flags; ++ ++}; ++ ++/** PS_CMD_ConfirmSleep */ ++typedef struct _PS_CMD_ConfirmSleep ++{ ++ /** SDIO Length */ ++ u16 SDLen; ++ /** SDIO Type */ ++ u16 SDType; ++ /** Command */ ++ u16 Command; ++ /** Size */ ++ u16 Size; ++ /** Sequence number */ ++ u16 SeqNum; ++ /** Result */ ++ u16 Result; ++} __ATTRIB_PACK__ PS_CMD_ConfirmSleep, *PPS_CMD_ConfirmSleep; ++ ++/** Wlan Adapter data structure*/ ++struct _uap_adapter ++{ ++ /** Power save confirm sleep command */ ++ PS_CMD_ConfirmSleep PSConfirmSleep; ++ /** Device status */ ++ HARDWARE_STATUS HardwareStatus; ++ /** Interrupt counter */ ++ u32 IntCounter; ++ /** Tx packet queue */ ++ struct sk_buff_head tx_queue; ++ /** Cmd packet queue */ ++ struct sk_buff_head cmd_queue; ++ /** Command sequence number */ ++ u16 SeqNum; ++ /** Command buffer */ ++ u8 *CmdBuf; ++ /** cmd pending flag */ ++ u8 cmd_pending; ++ /** cmd wait option */ ++ u8 cmd_wait_option; ++ /** Command buffer length */ ++ u32 CmdSize; ++ /** Command wait queue */ ++ wait_queue_head_t cmdwait_q __ATTRIB_ALIGN__; ++ /** Command wait queue state flag */ ++ u8 CmdWaitQWoken; ++ /** PnP support */ ++ BOOLEAN SurpriseRemoved; ++ /** Debug */ ++ uap_dbg dbg; ++ /** Netlink kernel socket */ ++ struct sock *nl_sk; ++ /** Semaphore for CMD */ ++ SEMAPHORE CmdSem; ++ /** Power Save mode */ ++ u8 psmode; ++ /** Power Save state */ ++ u8 ps_state; ++ /** Number of wakeup tries */ ++ u32 WakeupTries; ++}; ++ ++static inline int ++os_upload_rx_packet(uap_private * priv, struct sk_buff *skb) ++{ ++ skb->dev = priv->uap_dev.netdev; ++ skb->protocol = eth_type_trans(skb, priv->uap_dev.netdev); ++ skb->ip_summed = CHECKSUM_UNNECESSARY; ++ if (in_interrupt()) ++ netif_rx(skb); ++ else ++ netif_rx_ni(skb); ++ return 0; ++} ++ ++/* ++ * netif carrier_on/off and start(wake)/stop_queue handling ++ */ ++static inline void ++os_carrier_on(uap_private * priv) ++{ ++ if (!netif_carrier_ok(priv->uap_dev.netdev) && ++ (priv->MediaConnected == TRUE)) { ++ netif_carrier_on(priv->uap_dev.netdev); ++ } ++} ++ ++static inline void ++os_carrier_off(uap_private * priv) ++{ ++ if (netif_carrier_ok(priv->uap_dev.netdev)) { ++ netif_carrier_off(priv->uap_dev.netdev); ++ } ++} ++ ++static inline void ++os_start_queue(uap_private * priv) ++{ ++ if (netif_queue_stopped(priv->uap_dev.netdev) && ++ (priv->MediaConnected == TRUE)) { ++ netif_wake_queue(priv->uap_dev.netdev); ++ } ++} ++ ++static inline void ++os_stop_queue(uap_private * priv) ++{ ++ if (!netif_queue_stopped(priv->uap_dev.netdev)) { ++ netif_stop_queue(priv->uap_dev.netdev); ++ } ++} ++ ++/** Interface specific header */ ++#define INTF_HEADER_LEN 4 ++ ++/** headroom alignment for tx packet */ ++#define HEADER_ALIGNMENT 8 ++ ++/** The number of times to try when polling for status bits */ ++#define MAX_POLL_TRIES 100 ++ ++/** Length of SNAP header */ ++#define MRVDRV_SNAP_HEADER_LEN 8 ++ ++/** Extra length of Tx packet buffer */ ++#define EXTRA_LEN 36 ++ ++/** Buffer size for ethernet Tx packets */ ++#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ ++ (ETH_FRAME_LEN + sizeof(TxPD) + EXTRA_LEN) ++ ++/** Buffer size for ethernet Rx packets */ ++#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ ++ (ETH_FRAME_LEN + sizeof(RxPD) \ ++ + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) ++ ++/** Packet type: data, command & event */ ++typedef enum _mv_type ++{ ++ MV_TYPE_DAT = 0, ++ MV_TYPE_CMD = 1, ++ MV_TYPE_EVENT = 3 ++} mv_type; ++ ++/** 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) ++ ++int uap_process_rx_packet(uap_private * priv, struct sk_buff *skb); ++void uap_interrupt(uap_private * priv); ++uap_private *uap_add_card(void *card); ++int uap_remove_card(void *card); ++int uap_process_event(uap_private * priv, u8 * payload, uint len); ++int uap_soft_reset(uap_private * priv); ++int uap_process_sleep_confirm_resp(uap_private * priv, u8 * resp, int resp_len); ++ ++#ifdef CONFIG_PROC_FS ++/** The proc fs interface */ ++void uap_proc_entry(uap_private * priv, struct net_device *dev); ++void uap_proc_remove(uap_private * priv); ++int string_to_number(char *s); ++void uap_debug_entry(uap_private * priv, struct net_device *dev); ++void uap_debug_remove(uap_private * priv); ++#endif /* CONFIG_PROC_FS */ ++ ++int sbi_register(void); ++ ++void sbi_unregister(void); ++int sbi_register_dev(uap_private * priv); ++int sbi_unregister_dev(uap_private * priv); ++int sbi_prog_fw_w_helper(uap_private *); ++ ++int sbi_host_to_card(uap_private * priv, u8 * payload, u16 nb); ++int sbi_enable_host_int(uap_private * priv); ++int sbi_disable_host_int(uap_private * priv); ++ ++int sbi_get_int_status(uap_private * priv, u8 * ireg); ++/** Check firmware status */ ++int sbi_check_fw_status(uap_private *, int); ++int sbi_prog_helper(uap_private *); ++ ++int sbi_wakeup_firmware(uap_private * priv); ++ ++#endif /* _UAP_DRV_H */ +diff --git a/drivers/net/wireless/libertas_uap/uap_fw.h b/drivers/net/wireless/libertas_uap/uap_fw.h +new file mode 100644 +index 0000000..23a40d6 +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_fw.h +@@ -0,0 +1,359 @@ ++/** @file uap_fw.h ++ * ++ * @brief This file contains firmware specific defines. ++ * ++ * Copyright (C) 2008-2009, 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: ++ 02/26/08: Initial creation ++********************************************************/ ++ ++#ifndef _UAP_FW_H ++#define _UAP_FW_H ++ ++/** uap upload size */ ++#define UAP_UPLD_SIZE 2312 ++/** Packet type Micro AP */ ++#define PKT_TYPE_MICROAP 1 ++/** Packet type client */ ++#define PKT_TYPE_CLIENT 0 ++ ++/** TxPD descriptor */ ++typedef struct _TxPD ++{ ++ /** Bss Type */ ++ u8 BssType; ++ /** Bss num */ ++ u8 BssNum; ++ /** Tx packet length */ ++ u16 TxPktLength; ++ /** Tx packet offset */ ++ u16 TxPktOffset; ++ /** Tx packet type */ ++ u16 TxPktType; ++ /** Tx Control */ ++ u32 TxControl; ++ /** reserved */ ++ u32 reserved[2]; ++} __ATTRIB_PACK__ TxPD, *PTxPD; ++ ++/** RxPD Descriptor */ ++typedef struct _RxPD ++{ ++ /** Bss Type */ ++ u8 BssType; ++ /** Bss Num */ ++ u8 BssNum; ++ /** Tx packet length */ ++ u16 RxPktLength; ++ /** Tx packet offset */ ++ u16 RxPktOffset; ++} __ATTRIB_PACK__ RxPD, *PRxPD; ++ ++#ifdef BIG_ENDIAN ++/** Convert from 16 bit little endian format to CPU format */ ++#define uap_le16_to_cpu(x) le16_to_cpu(x) ++/** Convert from 32 bit little endian format to CPU format */ ++#define uap_le32_to_cpu(x) le32_to_cpu(x) ++/** Convert from 64 bit little endian format to CPU format */ ++#define uap_le64_to_cpu(x) le64_to_cpu(x) ++/** Convert to 16 bit little endian format from CPU format */ ++#define uap_cpu_to_le16(x) cpu_to_le16(x) ++/** Convert to 32 bit little endian format from CPU format */ ++#define uap_cpu_to_le32(x) cpu_to_le32(x) ++/** Convert to 64 bit little endian format from CPU format */ ++#define uap_cpu_to_le64(x) cpu_to_le64(x) ++ ++/** Convert TxPD to little endian format from CPU format */ ++#define endian_convert_TxPD(x); \ ++ { \ ++ (x)->TxPktLength = uap_cpu_to_le16((x)->TxPktLength); \ ++ (x)->TxPktOffset = uap_cpu_to_le32((x)->TxPktOffset); \ ++ (x)->TxControl = uap_cpu_to_le32((x)->TxControl); \ ++ (x)->TxPktType = uap_cpu_to_le32((x)->TxPktType); \ ++ } ++ ++/** Convert RxPD from little endian format to CPU format */ ++#define endian_convert_RxPD(x); \ ++ { \ ++ (x)->RxPktLength = uap_le16_to_cpu((x)->RxPktLength); \ ++ (x)->RxPktOffset = uap_le32_to_cpu((x)->RxPktOffset); \ ++ } ++#else /* BIG_ENDIAN */ ++/** 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 ++ ++/** Do nothing */ ++#define endian_convert_TxPD(x) ++/** Do nothing */ ++#define endian_convert_RxPD(x) ++#endif /* BIG_ENDIAN */ ++ ++/** Host Command ID : Function initialization */ ++#define HostCmd_CMD_FUNC_INIT 0x00a9 ++/** Host Command ID : Function shutdown */ ++#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa ++ ++/** Host Command id: SYS_INFO */ ++#define HOST_CMD_APCMD_SYS_INFO 0x00ae ++/** Host Command id: SYS_RESET */ ++#define HOST_CMD_APCMD_SYS_RESET 0x00af ++/** Host Command id: SYS_CONFIGURE */ ++#define HOST_CMD_APCMD_SYS_CONFIGURE 0x00b0 ++/** Host Command id: BSS_START */ ++#define HOST_CMD_APCMD_BSS_START 0x00b1 ++/** Host Command id: SYS_STOP */ ++#define HOST_CMD_APCMD_BSS_STOP 0x00b2 ++/** Host Command id: STA_LIST */ ++#define HOST_CMD_APCMD_STA_LIST 0x00b3 ++/** Host Command id: STA_FILTER_TABLE */ ++#define HOST_CMD_APCMD_STA_FILTER_TABLE 0x00b4 ++/** Host Command id: STA_DEAUTH */ ++#define HOST_CMD_APCMD_STA_DEAUTH 0x00b5 ++/** Host Command id: SOFT_RESET */ ++#define HOST_CMD_APCMD_SOFT_RESET 0x00d5 ++/** Host Command id: POWER_MGMT_EXT */ ++#define HOST_CMD_POWER_MGMT_EXT 0x00ef ++/** Host Command id: SLEEP_CONFIRM*/ ++#define HOST_CMD_SLEEP_CONFIRM 0x00d8 ++ ++/** TLV type : SSID */ ++#define TLV_TYPE_SSID 0x0000 ++/** TLV type : Rates */ ++#define TLV_TYPE_RATES 0x0001 ++/** TLV type : PHY DS */ ++#define TLV_TYPE_PHY_DS 0x0003 ++ ++/** TLV Id : Base id */ ++#define PROPRIETARY_TLV_BASE_ID 0x0100 ++/** TLV Id : AP_MAC_ADDRESS */ ++#define MRVL_AP_MAC_ADDRESS_TLV_ID (PROPRIETARY_TLV_BASE_ID + 43) ++/** TLV Id : Beacon period */ ++#define MRVL_BEACON_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 44) ++/** TLV Id : Dtim period */ ++#define MRVL_DTIM_PERIOD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 45) ++/** TLV Id : Basic rates */ ++#define MRVL_BASIC_RATES_TLV_ID (PROPRIETARY_TLV_BASE_ID + 46) ++/** TLV Id : Tx Power */ ++#define MRVL_TX_POWER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 47) ++/** TLV Id : Broadcast SSID control */ ++#define MRVL_BCAST_SSID_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 48) ++/** TLV Id : Preamble control */ ++#define MRVL_PREAMBLE_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 49) ++/** TLV Id : Antenna control */ ++#define MRVL_ANTENNA_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 50) ++/** TLV Id : RTS threshold */ ++#define MRVL_RTS_THRESHOLD_TLV_ID (PROPRIETARY_TLV_BASE_ID + 51) ++/** TLV Id : Radio control */ ++#define MRVL_RADIO_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 52) ++/** TLV Id : TX data rate */ ++#define MRVL_TX_DATA_RATE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 53) ++/** TLV Id : Packet forward control */ ++#define MRVL_PKT_FWD_CTL_TLV_ID (PROPRIETARY_TLV_BASE_ID + 54) ++/** TLV Id : STA info */ ++#define MRVL_STA_INFO_TLV_ID (PROPRIETARY_TLV_BASE_ID + 55) ++/** TLV Id : STA MAC address filter */ ++#define MRVL_STA_MAC_ADDR_FILTER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 56) ++/** TLV Id : STA ageout timer */ ++#define MRVL_STA_AGEOUT_TIMER_TLV_ID (PROPRIETARY_TLV_BASE_ID + 57) ++/** TLV Id : Security config */ ++#define MRVL_SECURITY_CFG_TLV_ID (PROPRIETARY_TLV_BASE_ID + 58) ++/** TLV Id : WEP KEY */ ++#define MRVL_WEP_KEY_TLV_ID (PROPRIETARY_TLV_BASE_ID + 59) ++/** TLV Id : WPA Passphrase */ ++#define MRVL_WPA_PASSPHRASE_TLV_ID (PROPRIETARY_TLV_BASE_ID + 60) ++ ++/** Action get */ ++#define ACTION_GET 0 ++/** Action set */ ++#define ACTION_SET 1 ++/** Length of ethernet address */ ++#ifndef ETH_ALEN ++#define ETH_ALEN 6 ++#endif ++ ++/** HostCmd_DS_GEN */ ++typedef struct ++{ ++ /** Command */ ++ u16 Command; ++ /** Size */ ++ u16 Size; ++ /** Sequence number */ ++ u16 SeqNum; ++ /** Result */ ++ u16 Result; ++} __ATTRIB_PACK__ HostCmd_DS_GEN; ++ ++/** Size of HostCmd_DS_GEN */ ++#define S_DS_GEN sizeof(HostCmd_DS_GEN) ++ ++/** _HostCmd_HEADER*/ ++typedef struct ++{ ++ /** Command Header : Command */ ++ u16 Command; ++ /** Command Header : Size */ ++ u16 Size; ++} __ATTRIB_PACK__ HostCmd_HEADER; ++ ++/** HostCmd_SYS_CONFIG */ ++typedef struct _HostCmd_SYS_CONFIG ++{ ++ /** CMD Action GET/SET*/ ++ u16 Action; ++ /** Tlv buffer */ ++ u8 TlvBuffer[0]; ++} __ATTRIB_PACK__ HostCmd_SYS_CONFIG; ++ ++/** HostCmd_DS_POWER_MGMT_EXT */ ++typedef struct _HostCmd_DS_POWER_MGMT_EXT ++{ ++ /** CMD Action Get/Set*/ ++ u16 action; ++ /** power mode */ ++ u16 power_mode; ++} __ATTRIB_PACK__ HostCmd_DS_POWER_MGMT_EXT; ++ ++/** _HostCmd_DS_COMMAND*/ ++typedef struct _HostCmd_DS_COMMAND ++{ ++ ++ /** Command Header : Command */ ++ u16 Command; ++ /** Command Header : Size */ ++ u16 Size; ++ /** Command Header : Sequence number */ ++ u16 SeqNum; ++ /** Command Header : Result */ ++ u16 Result; ++ /** Command Body */ ++ union ++ { ++ HostCmd_SYS_CONFIG sys_config; ++ HostCmd_DS_POWER_MGMT_EXT pm_cfg; ++ ++ } params; ++} __ATTRIB_PACK__ HostCmd_DS_COMMAND; ++ ++/** MrvlIEtypesHeader_*/ ++typedef struct _MrvlIEtypesHeader ++{ ++ /** Header type */ ++ u16 Type; ++ /** Header length */ ++ u16 Len; ++} __ATTRIB_PACK__ MrvlIEtypesHeader_t; ++ ++/** MrvlIEtypes_Data_t */ ++typedef struct _MrvlIEtypes_Data_t ++{ ++ /** Header */ ++ MrvlIEtypesHeader_t Header; ++ /** Data */ ++ u8 Data[1]; ++} __ATTRIB_PACK__ MrvlIEtypes_Data_t; ++ ++/** MrvlIEtypes_ChanListParamSet_t */ ++typedef struct _MrvlIEtypes_MacAddr_t ++{ ++ /** Header */ ++ MrvlIEtypesHeader_t Header; ++ /** AP MAC address */ ++ u8 ApMacAddr[ETH_ALEN]; ++} __ATTRIB_PACK__ MrvlIEtypes_MacAddr_t; ++ ++/** Event ID: BSS started */ ++#define MICRO_AP_EV_ID_BSS_START 46 ++ ++/** Event ID: BSS idle event */ ++#define MICRO_AP_EV_BSS_IDLE 67 ++ ++/** Event ID: BSS active event */ ++#define MICRO_AP_EV_BSS_ACTIVE 68 ++ ++/** Event ID: PS_AWAKE */ ++#define EVENT_PS_AWAKE 0x0a ++ ++/** Event ID: PS_SLEEP */ ++#define EVENT_PS_SLEEP 0x0b ++ ++/** PS_STATE */ ++typedef enum _PS_STATE ++{ ++ PS_STATE_AWAKE, ++ PS_STATE_PRE_SLEEP, ++ PS_STATE_SLEEP ++} PS_STATE; ++ ++/** TLV type: AP Sleep param */ ++#define TLV_TYPE_AP_SLEEP_PARAM (PROPRIETARY_TLV_BASE_ID + 106) ++/** TLV type: AP Inactivity Sleep param */ ++#define TLV_TYPE_AP_INACT_SLEEP_PARAM (PROPRIETARY_TLV_BASE_ID + 107) ++ ++/** MrvlIEtypes_sleep_param_t */ ++typedef struct _MrvlIEtypes_sleep_param_t ++{ ++ /** Header */ ++ MrvlIEtypesHeader_t header; ++ /** control bitmap */ ++ u32 ctrl_bitmap; ++ /** min_sleep */ ++ u32 min_sleep; ++ /** max_sleep */ ++ u32 max_sleep; ++} __ATTRIB_PACK__ MrvlIEtypes_sleep_param_t; ++ ++/** MrvlIEtypes_inact_sleep_param_t */ ++typedef struct _MrvlIEtypes_inact_sleep_param_t ++{ ++ /** Header */ ++ MrvlIEtypesHeader_t header; ++ /** inactivity timeout */ ++ u32 inactivity_to; ++ /** min_awake */ ++ u32 min_awake; ++ /** max_awake */ ++ u32 max_awake; ++} __ATTRIB_PACK__ MrvlIEtypes_inact_sleep_param_t; ++ ++/** AP_Event */ ++typedef struct _AP_Event ++{ ++ /** Event ID */ ++ u32 EventId; ++ /* ++ * Reserved for STA_ASSOCIATED event and contains ++ * status information for the MIC_COUNTERMEASURES event. ++ */ ++ /** Reserved/status */ ++ u16 status; ++ /** AP MAC address */ ++ u8 MacAddr[ETH_ALEN]; ++} __ATTRIB_PACK__ AP_Event; ++#endif /* _UAP_FW_H */ +diff --git a/drivers/net/wireless/libertas_uap/uap_headers.h b/drivers/net/wireless/libertas_uap/uap_headers.h +new file mode 100644 +index 0000000..fa09af4 +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_headers.h +@@ -0,0 +1,64 @@ ++/** @file uap_headers.h ++ * ++ * @brief This file contains all the necessary include file. ++ * ++ * Copyright (C) 2008-2009, 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 _UAP_HEADERS_H ++#define _UAP_HEADERS_H ++ ++/* Linux header files */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/version.h> ++#include <linux/param.h> ++#include <linux/types.h> ++#include <linux/interrupt.h> ++#include <linux/proc_fs.h> ++#include <linux/kthread.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) ++#include <linux/semaphore.h> ++#else ++#include <asm/semaphore.h> ++#endif ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++#include <linux/config.h> ++#endif ++ ++/* Net header files */ ++#include <linux/netdevice.h> ++#include <linux/net.h> ++#include <linux/skbuff.h> ++#include <linux/if_ether.h> ++#include <linux/etherdevice.h> ++#include <net/sock.h> ++#include <linux/netlink.h> ++#include <linux/firmware.h> ++#include <linux/delay.h> ++ ++#include "uap_drv.h" ++#include "uap_fw.h" ++ ++#include <linux/mmc/sdio.h> ++#include <linux/mmc/sdio_ids.h> ++#include <linux/mmc/sdio_func.h> ++#include <linux/mmc/card.h> ++#include "uap_sdio_mmc.h" ++ ++#endif /* _UAP_HEADERS_H */ +diff --git a/drivers/net/wireless/libertas_uap/uap_main.c b/drivers/net/wireless/libertas_uap/uap_main.c +new file mode 100644 +index 0000000..6ad8403 +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_main.c +@@ -0,0 +1,1817 @@ ++/** @file uap_main.c ++ * @brief This file contains the major functions in uAP ++ * driver. It includes init, exit etc.. ++ * This file also contains the initialization for SW, ++ * FW and HW ++ * ++ * Copyright (C) 2008-2009, 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 uAP Linux Driver ++ * ++ * @section overview_sec Overview ++ * ++ * This is Linux reference driver for Marvell uAP. ++ * ++ * @section copyright_sec Copyright ++ * ++ * Copyright (C) 2008, Marvell International Ltd. ++ * ++ */ ++ ++#include "uap_headers.h" ++ ++/** ++ * the global variable of a pointer to uap_private ++ * structure variable ++ */ ++uap_private *uappriv = NULL; ++#ifdef DEBUG_LEVEL1 ++#define DEFAULT_DEBUG_MASK (DBG_MSG | DBG_FATAL | DBG_ERROR) ++u32 drvdbg = DEFAULT_DEBUG_MASK; ++#endif ++/** Helper name */ ++char *helper_name = NULL; ++/** Firmware name */ ++char *fw_name = NULL; ++ ++/** Semaphore for add/remove card */ ++SEMAPHORE AddRemoveCardSem; ++ ++/******************************************************** ++ Local Functions ++********************************************************/ ++/** ++ * @brief This function send sleep confirm command to firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE ++ */ ++static int ++uap_dnld_sleep_confirm_cmd(uap_private * priv) ++{ ++ uap_adapter *Adapter = priv->adapter; ++ int ret = UAP_STATUS_SUCCESS; ++ ENTER(); ++ PRINTM(CMND, "Sleep confirm\n"); ++ Adapter->cmd_pending = TRUE; ++ Adapter->cmd_wait_option = HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM; ++ ret = ++ sbi_host_to_card(priv, (u8 *) & Adapter->PSConfirmSleep, ++ sizeof(PS_CMD_ConfirmSleep)); ++ if (ret != UAP_STATUS_SUCCESS) { ++ Adapter->ps_state = PS_STATE_AWAKE; ++ Adapter->cmd_pending = FALSE; ++ Adapter->cmd_wait_option = FALSE; ++ } ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function process sleep confirm resp from firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @param resp A pointer to resp buf ++ * @param resp_len resp buf len ++ * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE ++ */ ++int ++uap_process_sleep_confirm_resp(uap_private * priv, u8 * resp, int resp_len) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ HostCmd_DS_COMMAND *cmd; ++ uap_adapter *Adapter = priv->adapter; ++ ENTER(); ++ PRINTM(CMND, "Sleep confirm resp\n"); ++ if (!resp_len) { ++ PRINTM(ERROR, "Cmd Size is 0\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ cmd = (HostCmd_DS_COMMAND *) resp; ++ cmd->Result = uap_le16_to_cpu(cmd->Result); ++ if (cmd->Result != UAP_STATUS_SUCCESS) { ++ PRINTM(ERROR, "HOST_CMD_APCMD_PS_SLEEP_CONFIRM fail=%x\n", cmd->Result); ++ ret = -EFAULT; ++ } ++ done: ++ if (ret == UAP_STATUS_SUCCESS) ++ Adapter->ps_state = PS_STATE_SLEEP; ++ else ++ Adapter->ps_state = PS_STATE_AWAKE; ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function checks condition and prepares to ++ * send sleep confirm command to firmware if OK. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return n/a ++ */ ++static void ++uap_ps_cond_check(uap_private * priv) ++{ ++ uap_adapter *Adapter = priv->adapter; ++ ++ ENTER(); ++ if (!priv->uap_dev.cmd_sent && ++ !Adapter->cmd_pending && !Adapter->IntCounter) { ++ uap_dnld_sleep_confirm_cmd(priv); ++ } else { ++ PRINTM(INFO, "Delay Sleep Confirm (%s%s%s)\n", ++ (priv->uap_dev.cmd_sent) ? "D" : "", ++ (Adapter->cmd_pending) ? "C" : "", ++ (Adapter->IntCounter) ? "I" : ""); ++ } ++ LEAVE(); ++} ++ ++/** ++ * @brief This function add cmd to cmdQ and waiting for response ++ * ++ * @param priv A pointer to uap_private structure ++ * @param skb A pointer to the skb for process ++ * @param wait_option Wait option ++ * @return UAP_STATUS_SUCCESS for success otherwise UAP_STATUS_FAILURE ++ */ ++static int ++uap_process_cmd(uap_private * priv, struct sk_buff *skb, u8 wait_option) ++{ ++ uap_adapter *Adapter = priv->adapter; ++ int ret = UAP_STATUS_SUCCESS; ++ HostCmd_DS_COMMAND *cmd; ++ u8 *headptr; ++ ENTER(); ++ if (Adapter->HardwareStatus != HWReady) { ++ PRINTM(ERROR, "Hw not ready, uap_process_cmd\n"); ++ kfree(skb); ++ LEAVE(); ++ return -EFAULT; ++ } ++ skb->cb[0] = wait_option; ++ headptr = skb->data; ++ *(u16 *) & headptr[0] = uap_cpu_to_le16(skb->len); ++ *(u16 *) & headptr[2] = uap_cpu_to_le16(MV_TYPE_CMD); ++ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); ++ Adapter->SeqNum++; ++ cmd->SeqNum = uap_cpu_to_le16(Adapter->SeqNum); ++ PRINTM(CMND, "process_cmd: %x\n", cmd->Command); ++ DBG_HEXDUMP(CMD_D, "process_cmd", (u8 *) cmd, cmd->Size); ++ if (!wait_option) { ++ skb_queue_tail(&priv->adapter->cmd_queue, skb); ++ wake_up_interruptible(&priv->MainThread.waitQ); ++ LEAVE(); ++ return ret; ++ } ++ if (OS_ACQ_SEMAPHORE_BLOCK(&Adapter->CmdSem)) { ++ PRINTM(ERROR, "Acquire semaphore error, uap_prepare_cmd\n"); ++ kfree(skb); ++ LEAVE(); ++ return -EBUSY; ++ } ++ skb_queue_tail(&priv->adapter->cmd_queue, skb); ++ Adapter->CmdWaitQWoken = FALSE; ++ wake_up_interruptible(&priv->MainThread.waitQ); ++ /* Sleep until response is generated by FW */ ++ if (wait_option == HostCmd_OPTION_WAITFORRSP_TIMEOUT) { ++ if (!os_wait_interruptible_timeout ++ (Adapter->cmdwait_q, Adapter->CmdWaitQWoken, MRVDRV_TIMER_20S)) { ++ PRINTM(ERROR, "Cmd timeout\n"); ++ Adapter->cmd_pending = FALSE; ++ ret = -EFAULT; ++ } ++ } else ++ wait_event_interruptible(Adapter->cmdwait_q, Adapter->CmdWaitQWoken); ++ OS_REL_SEMAPHORE(&Adapter->CmdSem); ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief Inspect the response buffer for pointers to expected TLVs ++ * ++ * ++ * @param pTlv Pointer to the start of the TLV buffer to parse ++ * @param tlvBufSize Size of the TLV buffer ++ * @param reqTlvType request tlv's tlvtype ++ * @param ppTlv Output parameter: Pointer to the request TLV if found ++ * ++ * @return void ++ */ ++static void ++uap_get_tlv_ptrs(MrvlIEtypes_Data_t * pTlv, int tlvBufSize, ++ u16 reqTlvType, MrvlIEtypes_Data_t ** ppTlv) ++{ ++ MrvlIEtypes_Data_t *pCurrentTlv; ++ int tlvBufLeft; ++ u16 tlvType; ++ u16 tlvLen; ++ ++ ENTER(); ++ pCurrentTlv = pTlv; ++ tlvBufLeft = tlvBufSize; ++ *ppTlv = NULL; ++ PRINTM(INFO, "uap_get_tlv: tlvBufSize = %d, reqTlvType=%x\n", tlvBufSize, ++ reqTlvType); ++ while (tlvBufLeft >= sizeof(MrvlIEtypesHeader_t)) { ++ tlvType = uap_le16_to_cpu(pCurrentTlv->Header.Type); ++ tlvLen = uap_le16_to_cpu(pCurrentTlv->Header.Len); ++ if (reqTlvType == tlvType) ++ *ppTlv = (MrvlIEtypes_Data_t *) pCurrentTlv; ++ if (*ppTlv) { ++ HEXDUMP("TLV Buf", (u8 *) * ppTlv, tlvLen); ++ break; ++ } ++ tlvBufLeft -= (sizeof(pTlv->Header) + tlvLen); ++ pCurrentTlv = (MrvlIEtypes_Data_t *) (pCurrentTlv->Data + tlvLen); ++ } /* while */ ++ LEAVE(); ++} ++ ++/** ++ * @brief This function get mac ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS on success, otherwise failure code ++ */ ++static int ++uap_get_mac_address(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u32 CmdSize; ++ HostCmd_DS_COMMAND *cmd; ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb; ++ MrvlIEtypes_MacAddr_t *pMacAddrTlv; ++ MrvlIEtypes_Data_t *pTlv; ++ u16 tlvBufSize; ++ ENTER(); ++ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); ++ if (!skb) { ++ PRINTM(ERROR, "No free skb\n"); ++ ret = -ENOMEM; ++ goto done; ++ } ++ CmdSize = ++ S_DS_GEN + sizeof(HostCmd_SYS_CONFIG) + sizeof(MrvlIEtypes_MacAddr_t); ++ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); ++ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); ++ cmd->Size = uap_cpu_to_le16(CmdSize); ++ cmd->params.sys_config.Action = uap_cpu_to_le16(ACTION_GET); ++ pMacAddrTlv = ++ (MrvlIEtypes_MacAddr_t *) (skb->data + INTF_HEADER_LEN + S_DS_GEN + ++ sizeof(HostCmd_SYS_CONFIG)); ++ pMacAddrTlv->Header.Type = uap_cpu_to_le16(MRVL_AP_MAC_ADDRESS_TLV_ID); ++ pMacAddrTlv->Header.Len = uap_cpu_to_le16(ETH_ALEN); ++ skb_put(skb, CmdSize + INTF_HEADER_LEN); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { ++ PRINTM(ERROR, "Fail to process cmd SYS_CONFIGURE Query\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ if (!Adapter->CmdSize) { ++ PRINTM(ERROR, "Cmd Size is 0\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; ++ cmd->Result = uap_le16_to_cpu(cmd->Result); ++ if (cmd->Result != UAP_STATUS_SUCCESS) { ++ PRINTM(ERROR, "uap_get_mac_address fail=%x\n", cmd->Result); ++ ret = -EFAULT; ++ goto done; ++ } ++ pTlv = ++ (MrvlIEtypes_Data_t *) (Adapter->CmdBuf + S_DS_GEN + ++ sizeof(HostCmd_SYS_CONFIG)); ++ tlvBufSize = Adapter->CmdSize - S_DS_GEN - sizeof(HostCmd_SYS_CONFIG); ++ uap_get_tlv_ptrs(pTlv, tlvBufSize, MRVL_AP_MAC_ADDRESS_TLV_ID, ++ (MrvlIEtypes_Data_t **) & pMacAddrTlv); ++ if (pMacAddrTlv) { ++ memcpy(priv->uap_dev.netdev->dev_addr, pMacAddrTlv->ApMacAddr, ++ ETH_ALEN); ++ HEXDUMP("Original MAC addr", priv->uap_dev.netdev->dev_addr, ETH_ALEN); ++ } ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function checks the conditions and sends packet to device ++ * ++ * @param priv A pointer to uap_private structure ++ * @param skb A pointer to the skb for process ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++uap_process_tx(uap_private * priv, struct sk_buff *skb) ++{ ++ uap_adapter *Adapter = priv->adapter; ++ int ret = UAP_STATUS_SUCCESS; ++ TxPD *pLocalTxPD; ++ u8 *headptr; ++ struct sk_buff *newskb; ++ int newheadlen; ++ ENTER(); ++ ASSERT(skb); ++ if (!skb) { ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ if (skb_headroom(skb) < (sizeof(TxPD) + INTF_HEADER_LEN + HEADER_ALIGNMENT)) { ++ newheadlen = sizeof(TxPD) + INTF_HEADER_LEN + HEADER_ALIGNMENT; ++ PRINTM(WARN, "Tx: Insufficient skb headroom %d\n", skb_headroom(skb)); ++ /* Insufficient skb headroom - allocate a new skb */ ++ newskb = skb_realloc_headroom(skb, newheadlen); ++ if (unlikely(newskb == NULL)) { ++ PRINTM(ERROR, "Tx: Cannot allocate skb\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ kfree_skb(skb); ++ skb = newskb; ++ PRINTM(INFO, "new skb headroom %d\n", skb_headroom(skb)); ++ } ++ /* headptr should be aligned */ ++ headptr = skb->data - sizeof(TxPD) - INTF_HEADER_LEN; ++ headptr = (u8 *) ((u32) headptr & ~((u32) (HEADER_ALIGNMENT - 1))); ++ ++ pLocalTxPD = (TxPD *) (headptr + INTF_HEADER_LEN); ++ memset(pLocalTxPD, 0, sizeof(TxPD)); ++ pLocalTxPD->BssType = PKT_TYPE_MICROAP; ++ pLocalTxPD->TxPktLength = skb->len; ++ /* offset of actual data */ ++ pLocalTxPD->TxPktOffset = (long) skb->data - (long) pLocalTxPD; ++ endian_convert_TxPD(pLocalTxPD); ++ *(u16 *) & headptr[0] = ++ uap_cpu_to_le16(skb->len + ((long) skb->data - (long) headptr)); ++ *(u16 *) & headptr[2] = uap_cpu_to_le16(MV_TYPE_DAT); ++ ret = ++ sbi_host_to_card(priv, headptr, ++ skb->len + ((long) skb->data - (long) headptr)); ++ if (ret) { ++ PRINTM(ERROR, "uap_process_tx Error: sbi_host_to_card failed: 0x%X\n", ++ ret); ++ Adapter->dbg.num_tx_host_to_card_failure++; ++ goto done; ++ } ++ PRINTM(DATA, "Data => FW\n"); ++ DBG_HEXDUMP(DAT_D, "Tx", headptr, ++ MIN(skb->len + sizeof(TxPD), DATA_DUMP_LEN)); ++ done: ++ /* Freed skb */ ++ kfree_skb(skb); ++ LEAVE(); ++ return ret; ++} ++ ++static struct netlink_kernel_cfg cfg = { ++ .groups = NL_MULTICAST_GROUP, ++}; ++ ++/** ++ * @brief This function initializes the adapter structure ++ * and set default value to the member of adapter. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++uap_init_sw(uap_private * priv) ++{ ++ uap_adapter *Adapter = priv->adapter; ++ ++ ENTER(); ++ ++ if (!(Adapter->CmdBuf = kmalloc(MRVDRV_SIZE_OF_CMD_BUFFER, GFP_KERNEL))) { ++ PRINTM(INFO, "Failed to allocate command buffer!\n"); ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ ++ Adapter->cmd_pending = FALSE; ++ Adapter->CmdWaitQWoken = FALSE; ++ Adapter->ps_state = PS_STATE_AWAKE; ++ Adapter->WakeupTries = 0; ++ ++ memset(&Adapter->PSConfirmSleep, 0, sizeof(PS_CMD_ConfirmSleep)); ++ /** SDIO header */ ++ Adapter->PSConfirmSleep.SDLen = ++ uap_cpu_to_le16(sizeof(PS_CMD_ConfirmSleep)); ++ Adapter->PSConfirmSleep.SDType = uap_cpu_to_le16(MV_TYPE_CMD); ++ Adapter->PSConfirmSleep.SeqNum = 0; ++ Adapter->PSConfirmSleep.Command = uap_cpu_to_le16(HOST_CMD_SLEEP_CONFIRM); ++ Adapter->PSConfirmSleep.Size = uap_cpu_to_le16(sizeof(HostCmd_DS_GEN)); ++ Adapter->PSConfirmSleep.Result = 0; ++ ++ init_waitqueue_head(&Adapter->cmdwait_q); ++ OS_INIT_SEMAPHORE(&Adapter->CmdSem); ++ ++ skb_queue_head_init(&Adapter->tx_queue); ++ skb_queue_head_init(&Adapter->cmd_queue); ++ ++ /* Status variable */ ++ Adapter->HardwareStatus = HWInitializing; ++ ++ /* PnP support */ ++ Adapter->SurpriseRemoved = FALSE; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22) ++ Adapter->nl_sk = netlink_kernel_create(NETLINK_MARVELL, ++ NL_MULTICAST_GROUP, NULL, ++ THIS_MODULE); ++#else ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ++ Adapter->nl_sk = netlink_kernel_create(NETLINK_MARVELL, ++ NL_MULTICAST_GROUP, NULL, NULL, ++ THIS_MODULE); ++#else ++ Adapter->nl_sk = netlink_kernel_create(&init_net, NETLINK_MARVELL, &cfg); ++#endif ++#endif ++ if (!Adapter->nl_sk) { ++ PRINTM(ERROR, ++ "Could not initialize netlink event passing mechanism!\n"); ++ } ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++ ++/** ++ * @brief This function sends FUNC_INIT command to firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS on success, otherwise failure code ++ */ ++static int ++uap_func_init(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u32 CmdSize; ++ HostCmd_DS_GEN *cmd; ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb; ++ ENTER(); ++ if (Adapter->HardwareStatus != HWReady) { ++ PRINTM(ERROR, "uap_func_init:Hardware is not ready!\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); ++ if (!skb) { ++ PRINTM(ERROR, "No free skb\n"); ++ ret = -ENOMEM; ++ goto done; ++ } ++ CmdSize = sizeof(HostCmd_DS_GEN); ++ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); ++ cmd->Command = uap_cpu_to_le16(HostCmd_CMD_FUNC_INIT); ++ cmd->Size = uap_cpu_to_le16(CmdSize); ++ skb_put(skb, CmdSize + INTF_HEADER_LEN); ++ PRINTM(CMND, "HostCmd_CMD_FUNC_INIT\n"); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { ++ PRINTM(ERROR, "Fail to process cmd HostCmd_CMD_FUNC_INIT\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function sends FUNC_SHUTDOWN command to firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS on success, otherwise failure code ++ */ ++static int __exit ++uap_func_shutdown(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u32 CmdSize; ++ HostCmd_DS_GEN *cmd; ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb; ++ ENTER(); ++ if (Adapter->HardwareStatus != HWReady) { ++ PRINTM(ERROR, "uap_func_shutdown:Hardware is not ready!\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); ++ if (!skb) { ++ PRINTM(ERROR, "No free skb\n"); ++ ret = -ENOMEM; ++ goto done; ++ } ++ CmdSize = sizeof(HostCmd_DS_GEN); ++ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); ++ cmd->Command = uap_cpu_to_le16(HostCmd_CMD_FUNC_SHUTDOWN); ++ cmd->Size = uap_cpu_to_le16(CmdSize); ++ skb_put(skb, CmdSize + INTF_HEADER_LEN); ++ PRINTM(CMND, "HostCmd_CMD_FUNC_SHUTDOWN\n"); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { ++ PRINTM(ERROR, "Fail to process cmd HostCmd_CMD_FUNC_SHUTDOWN\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function initializes firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++uap_init_fw(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ ENTER(); ++ sbi_disable_host_int(priv); ++ /* Check if firmware is already running */ ++ if (sbi_check_fw_status(priv, 1) == UAP_STATUS_SUCCESS) { ++ PRINTM(MSG, "UAP FW already running! Skip FW download\n"); ++ } else { ++ if ((ret = request_firmware(&priv->fw_helper, helper_name, ++ priv->hotplug_device)) < 0) { ++ PRINTM(FATAL, ++ "request_firmware() failed (helper), error code = %#x\n", ++ ret); ++ goto done; ++ } ++ ++ /* Download the helper */ ++ ret = sbi_prog_helper(priv); ++ ++ if (ret) { ++ PRINTM(FATAL, ++ "Bootloader in invalid state! Helper download failed!\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ if ((ret = request_firmware(&priv->firmware, fw_name, ++ priv->hotplug_device)) < 0) { ++ PRINTM(FATAL, "request_firmware() failed, error code = %#x\n", ret); ++ goto done; ++ } ++ ++ /* Download the main firmware via the helper firmware */ ++ if (sbi_prog_fw_w_helper(priv)) { ++ PRINTM(FATAL, "UAP FW download failed!\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ /* Check if the firmware is downloaded successfully or not */ ++ if (sbi_check_fw_status(priv, MAX_FIRMWARE_POLL_TRIES) == ++ UAP_STATUS_FAILURE) { ++ PRINTM(FATAL, "FW failed to be active in time!\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ PRINTM(MSG, "UAP FW is active\n"); ++ } ++ sbi_enable_host_int(priv); ++ priv->adapter->HardwareStatus = HWReady; ++ if (uap_func_init(priv) != UAP_STATUS_SUCCESS) { ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ done: ++ if (priv->fw_helper) ++ release_firmware(priv->fw_helper); ++ if (priv->firmware) ++ release_firmware(priv->firmware); ++ LEAVE(); ++ return ret; ++ ++} ++ ++/** ++ * @brief This function frees the structure of adapter ++ * ++ * @param priv A pointer to uap_private structure ++ * @return n/a ++ */ ++static void ++uap_free_adapter(uap_private * priv) ++{ ++ uap_adapter *Adapter = priv->adapter; ++ ++ ENTER(); ++ ++ if (Adapter) { ++ if ((Adapter->nl_sk) && ((Adapter->nl_sk)->sk_socket)) { ++ sock_release((Adapter->nl_sk)->sk_socket); ++ Adapter->nl_sk = NULL; ++ } ++ if (Adapter->CmdBuf) ++ kfree(Adapter->CmdBuf); ++ skb_queue_purge(&priv->adapter->tx_queue); ++ skb_queue_purge(&priv->adapter->cmd_queue); ++ /* Free the adapter object itself */ ++ kfree(Adapter); ++ priv->adapter = NULL; ++ } ++ ++ LEAVE(); ++} ++ ++/** ++ * @brief This function handles the major job in uap driver. ++ * it handles the event generated by firmware, rx data received ++ * from firmware and tx data sent from kernel. ++ * ++ * @param data A pointer to uap_thread structure ++ * @return BT_STATUS_SUCCESS ++ */ ++static int ++uap_service_main_thread(void *data) ++{ ++ uap_thread *thread = data; ++ uap_private *priv = thread->priv; ++ uap_adapter *Adapter = priv->adapter; ++ wait_queue_t wait; ++ u8 ireg = 0; ++ struct sk_buff *skb; ++ ENTER(); ++ uap_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 ((Adapter->WakeupTries) || ++ (!Adapter->IntCounter && Adapter->ps_state == PS_STATE_PRE_SLEEP) || ++ (!priv->adapter->IntCounter ++ && (priv->uap_dev.data_sent || ++ skb_queue_empty(&priv->adapter->tx_queue)) ++ && (priv->uap_dev.cmd_sent || Adapter->cmd_pending || ++ skb_queue_empty(&priv->adapter->cmd_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); ++ /* Cancel pending command */ ++ if (Adapter->cmd_pending == TRUE) { ++ /* Wake up cmd Q */ ++ Adapter->CmdWaitQWoken = TRUE; ++ wake_up_interruptible(&Adapter->cmdwait_q); ++ } ++ 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, &ireg); ++ } else if ((priv->adapter->ps_state == PS_STATE_SLEEP) && ++ (!skb_queue_empty(&priv->adapter->cmd_queue) || ++ !skb_queue_empty(&priv->adapter->tx_queue))) { ++ priv->adapter->WakeupTries++; ++ PRINTM(CMND, "%lu : Wakeup device...\n", os_time_get()); ++ sbi_wakeup_firmware(priv); ++ continue; ++ } ++ if (Adapter->ps_state == PS_STATE_PRE_SLEEP) ++ uap_ps_cond_check(priv); ++ ++ /* The PS state is changed during processing of Sleep Request event ++ above */ ++ if ((Adapter->ps_state == PS_STATE_SLEEP) || ++ (Adapter->ps_state == PS_STATE_PRE_SLEEP)) ++ continue; ++ /* Execute the next command */ ++ if (!priv->uap_dev.cmd_sent && !Adapter->cmd_pending && ++ (Adapter->HardwareStatus == HWReady)) { ++ if (!skb_queue_empty(&priv->adapter->cmd_queue)) { ++ skb = skb_dequeue(&priv->adapter->cmd_queue); ++ if (skb) { ++ Adapter->CmdSize = 0; ++ Adapter->cmd_pending = TRUE; ++ Adapter->cmd_wait_option = skb->cb[0]; ++ if (sbi_host_to_card(priv, skb->data, skb->len)) { ++ PRINTM(ERROR, "Cmd:sbi_host_to_card failed!\n"); ++ Adapter->cmd_pending = FALSE; ++ Adapter->dbg.num_cmd_host_to_card_failure++; ++ /* Wake up cmd Q */ ++ Adapter->CmdWaitQWoken = TRUE; ++ wake_up_interruptible(&Adapter->cmdwait_q); ++ } else { ++ if (Adapter->cmd_wait_option == ++ HostCmd_OPTION_WAITFORSEND) { ++ /* Wake up cmd Q */ ++ Adapter->CmdWaitQWoken = TRUE; ++ wake_up_interruptible(&Adapter->cmdwait_q); ++ Adapter->cmd_wait_option = FALSE; ++ } ++ } ++ kfree_skb(skb); ++ } ++ } ++ } ++ if (!priv->uap_dev.data_sent && (Adapter->HardwareStatus == HWReady)) { ++ if (!skb_queue_empty(&priv->adapter->tx_queue)) { ++ skb = skb_dequeue(&priv->adapter->tx_queue); ++ if (skb) { ++ if (uap_process_tx(priv, skb)) { ++ priv->stats.tx_dropped++; ++ priv->stats.tx_errors++; ++ os_start_queue(priv); ++ } else { ++ priv->stats.tx_packets++; ++ priv->stats.tx_bytes += skb->len; ++ } ++ ++ } ++ } ++ } ++ } ++ uap_deactivate_thread(thread); ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++ ++/** ++ * @brief uap hostcmd ioctl handler ++ * ++ * @param dev A pointer to net_device structure ++ * @param req A pointer to ifreq structure ++ * @return UAP_STATUS_SUCCESS --success, otherwise fail ++ */ ++/********* format of ifr_data *************/ ++/* buf_len + Hostcmd_body */ ++/* buf_len: 4 bytes */ ++/* the length of the buf which */ ++/* can be used to return data */ ++/* to application */ ++/* Hostcmd_body */ ++/*******************************************/ ++static int ++uap_hostcmd_ioctl(struct net_device *dev, struct ifreq *req) ++{ ++ u32 buf_len; ++ HostCmd_HEADER head; ++ uap_private *priv = (uap_private *) netdev_priv(dev); ++ uap_adapter *Adapter = priv->adapter; ++ int ret = UAP_STATUS_SUCCESS; ++ struct sk_buff *skb; ++ ++ ENTER(); ++ ++ /* Sanity check */ ++ if (req->ifr_data == NULL) { ++ PRINTM(ERROR, "uap_hostcmd_ioctl() corrupt data\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ if (copy_from_user(&buf_len, req->ifr_data, sizeof(buf_len))) { ++ PRINTM(ERROR, "Copy from user failed\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ memset(&head, 0, sizeof(HostCmd_HEADER)); ++ /* Get the command size from user space */ ++ if (copy_from_user ++ (&head, req->ifr_data + sizeof(buf_len), sizeof(HostCmd_HEADER))) { ++ PRINTM(ERROR, "Copy from user failed\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ head.Size = uap_le16_to_cpu(head.Size); ++ if (head.Size > MRVDRV_SIZE_OF_CMD_BUFFER) { ++ PRINTM(ERROR, "CmdSize too big=%d\n", head.Size); ++ LEAVE(); ++ return -EFAULT; ++ } ++ PRINTM(CMND, "ioctl: hostcmd=%x, size=%d,buf_len=%d\n", head.Command, ++ head.Size, buf_len); ++ skb = dev_alloc_skb(head.Size + INTF_HEADER_LEN); ++ if (!skb) { ++ PRINTM(ERROR, "No free skb\n"); ++ LEAVE(); ++ return -ENOMEM; ++ } ++ ++ /* Get the command from user space */ ++ if (copy_from_user ++ (skb->data + INTF_HEADER_LEN, req->ifr_data + sizeof(buf_len), ++ head.Size)) { ++ PRINTM(ERROR, "Copy from user failed\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ skb_put(skb, head.Size + INTF_HEADER_LEN); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP)) { ++ PRINTM(ERROR, "Fail to process cmd\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ if (!Adapter->CmdSize) { ++ PRINTM(ERROR, "Cmd Size is 0\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ if (Adapter->CmdSize > buf_len) { ++ PRINTM(ERROR, "buf_len is too small\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ /* Copy to user */ ++ if (copy_to_user ++ (req->ifr_data + sizeof(buf_len), Adapter->CmdBuf, Adapter->CmdSize)) { ++ PRINTM(ERROR, "Copy to user failed!\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief uap power mode ioctl handler ++ * ++ * @param dev A pointer to net_device structure ++ * @param req A pointer to ifreq structure ++ * @return UAP_STATUS_SUCCESS --success, otherwise fail ++ */ ++static int ++uap_power_mode_ioctl(struct net_device *dev, struct ifreq *req) ++{ ++ ps_mgmt pm_cfg; ++ int ret = UAP_STATUS_SUCCESS; ++ uap_private *priv = (uap_private *) netdev_priv(dev); ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb = NULL; ++ HostCmd_DS_COMMAND *cmd; ++ u32 CmdSize; ++ u8 *tlv = NULL; ++ MrvlIEtypes_sleep_param_t *sleep_tlv = NULL; ++ MrvlIEtypes_inact_sleep_param_t *inact_tlv = NULL; ++ u16 tlv_buf_left = 0; ++ MrvlIEtypesHeader_t *tlvbuf = NULL; ++ u16 tlv_type = 0; ++ u16 tlv_len = 0; ++ ++ ENTER(); ++ ++ /* Sanity check */ ++ if (req->ifr_data == NULL) { ++ PRINTM(ERROR, "uap_power_mode_ioctl() corrupt data\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ ++ memset(&pm_cfg, 0, sizeof(ps_mgmt)); ++ if (copy_from_user(&pm_cfg, req->ifr_data, sizeof(ps_mgmt))) { ++ PRINTM(ERROR, "Copy from user failed\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ PRINTM(CMND, ++ "ioctl power: flag=0x%x ps_mode=%d ctrl_bitmap=%d min_sleep=%d max_sleep=%d " ++ "inact_to=%d min_awake=%d max_awake=%d\n", pm_cfg.flags, ++ (int) pm_cfg.ps_mode, (int) pm_cfg.sleep_param.ctrl_bitmap, ++ (int) pm_cfg.sleep_param.min_sleep, ++ (int) pm_cfg.sleep_param.max_sleep, ++ (int) pm_cfg.inact_param.inactivity_to, ++ (int) pm_cfg.inact_param.min_awake, ++ (int) pm_cfg.inact_param.max_awake); ++ ++ if (pm_cfg. ++ flags & ~(PS_FLAG_PS_MODE | PS_FLAG_SLEEP_PARAM | ++ PS_FLAG_INACT_SLEEP_PARAM)) { ++ PRINTM(ERROR, "Invalid parameter: flags = 0x%x\n", pm_cfg.flags); ++ ret = -EINVAL; ++ goto done; ++ } ++ if (pm_cfg.ps_mode > PS_MODE_INACTIVITY) { ++ PRINTM(ERROR, "Invalid parameter: ps_mode = %d\n", (int) pm_cfg.flags); ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); ++ if (!skb) { ++ PRINTM(INFO, "No free skb\n"); ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ CmdSize = S_DS_GEN + sizeof(HostCmd_DS_POWER_MGMT_EXT); ++ ++ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); ++ cmd->Command = uap_cpu_to_le16(HOST_CMD_POWER_MGMT_EXT); ++ if (!pm_cfg.flags) { ++ cmd->params.pm_cfg.action = uap_cpu_to_le16(ACTION_GET); ++ } else { ++ cmd->params.pm_cfg.action = uap_cpu_to_le16(ACTION_SET); ++ cmd->params.pm_cfg.power_mode = uap_cpu_to_le16(pm_cfg.ps_mode); ++ tlv = (u8 *) & cmd->params.pm_cfg + sizeof(HostCmd_DS_POWER_MGMT_EXT); ++ ++ if ((pm_cfg.ps_mode) && (pm_cfg.flags & PS_FLAG_SLEEP_PARAM)) { ++ sleep_tlv = (MrvlIEtypes_sleep_param_t *) tlv; ++ sleep_tlv->header.Type = uap_cpu_to_le16(TLV_TYPE_AP_SLEEP_PARAM); ++ sleep_tlv->header.Len = ++ uap_cpu_to_le16(sizeof(MrvlIEtypes_sleep_param_t) - ++ sizeof(MrvlIEtypesHeader_t)); ++ sleep_tlv->ctrl_bitmap = ++ uap_cpu_to_le32(pm_cfg.sleep_param.ctrl_bitmap); ++ sleep_tlv->min_sleep = ++ uap_cpu_to_le32(pm_cfg.sleep_param.min_sleep); ++ sleep_tlv->max_sleep = ++ uap_cpu_to_le32(pm_cfg.sleep_param.max_sleep); ++ CmdSize += sizeof(MrvlIEtypes_sleep_param_t); ++ tlv += sizeof(MrvlIEtypes_sleep_param_t); ++ } ++ if ((pm_cfg.ps_mode == PS_MODE_INACTIVITY) && ++ (pm_cfg.flags & PS_FLAG_INACT_SLEEP_PARAM)) { ++ inact_tlv = (MrvlIEtypes_inact_sleep_param_t *) tlv; ++ inact_tlv->header.Type = ++ uap_cpu_to_le16(TLV_TYPE_AP_INACT_SLEEP_PARAM); ++ inact_tlv->header.Len = ++ uap_cpu_to_le16(sizeof(MrvlIEtypes_inact_sleep_param_t) - ++ sizeof(MrvlIEtypesHeader_t)); ++ inact_tlv->inactivity_to = ++ uap_cpu_to_le32(pm_cfg.inact_param.inactivity_to); ++ inact_tlv->min_awake = ++ uap_cpu_to_le32(pm_cfg.inact_param.min_awake); ++ inact_tlv->max_awake = ++ uap_cpu_to_le32(pm_cfg.inact_param.max_awake); ++ CmdSize += sizeof(MrvlIEtypes_inact_sleep_param_t); ++ tlv += sizeof(MrvlIEtypes_inact_sleep_param_t); ++ } ++ } ++ cmd->Size = uap_cpu_to_le16(CmdSize); ++ skb_put(skb, CmdSize + INTF_HEADER_LEN); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP)) { ++ PRINTM(ERROR, "Fail to process cmd POWER_MODE\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ if (!Adapter->CmdSize) { ++ PRINTM(ERROR, "Cmd Size is 0\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; ++ cmd->Result = uap_le16_to_cpu(cmd->Result); ++ if (cmd->Result != UAP_STATUS_SUCCESS) { ++ PRINTM(ERROR, "HOST_CMD_APCMD_POWER_MODE fail=%x\n", cmd->Result); ++ ret = -EFAULT; ++ goto done; ++ } ++ if (pm_cfg.flags) { ++ Adapter->psmode = uap_le16_to_cpu(cmd->params.pm_cfg.power_mode); ++ } else { ++ pm_cfg.flags = PS_FLAG_PS_MODE; ++ pm_cfg.ps_mode = uap_le16_to_cpu(cmd->params.pm_cfg.power_mode); ++ tlv_buf_left = ++ cmd->Size - (sizeof(HostCmd_DS_POWER_MGMT_EXT) + S_DS_GEN); ++ tlvbuf = ++ (MrvlIEtypesHeader_t *) ((u8 *) & cmd->params.pm_cfg + ++ sizeof(HostCmd_DS_POWER_MGMT_EXT)); ++ while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { ++ tlv_type = uap_le16_to_cpu(tlvbuf->Type); ++ tlv_len = uap_le16_to_cpu(tlvbuf->Len); ++ switch (tlv_type) { ++ case TLV_TYPE_AP_SLEEP_PARAM: ++ sleep_tlv = (MrvlIEtypes_sleep_param_t *) tlvbuf; ++ pm_cfg.flags |= PS_FLAG_SLEEP_PARAM; ++ pm_cfg.sleep_param.ctrl_bitmap = ++ uap_le32_to_cpu(sleep_tlv->ctrl_bitmap); ++ pm_cfg.sleep_param.min_sleep = ++ uap_le32_to_cpu(sleep_tlv->min_sleep); ++ pm_cfg.sleep_param.max_sleep = ++ uap_le32_to_cpu(sleep_tlv->max_sleep); ++ break; ++ case TLV_TYPE_AP_INACT_SLEEP_PARAM: ++ inact_tlv = (MrvlIEtypes_inact_sleep_param_t *) tlvbuf; ++ pm_cfg.flags |= PS_FLAG_INACT_SLEEP_PARAM; ++ pm_cfg.inact_param.inactivity_to = ++ uap_le32_to_cpu(inact_tlv->inactivity_to); ++ pm_cfg.inact_param.min_awake = ++ uap_le32_to_cpu(inact_tlv->min_awake); ++ pm_cfg.inact_param.max_awake = ++ uap_le32_to_cpu(inact_tlv->max_awake); ++ break; ++ } ++ tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); ++ tlvbuf = ++ (MrvlIEtypesHeader_t *) ((u8 *) tlvbuf + tlv_len + ++ sizeof(MrvlIEtypesHeader_t)); ++ } ++ /* Copy to user */ ++ if (copy_to_user(req->ifr_data, &pm_cfg, sizeof(ps_mgmt))) { ++ PRINTM(ERROR, "Copy to user failed!\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ } ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function send bss_stop command to firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS on success, otherwise failure code ++ */ ++static int ++uap_bss_stop(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u32 CmdSize; ++ HostCmd_DS_GEN *cmd; ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb; ++ ENTER(); ++ if (Adapter->HardwareStatus != HWReady) { ++ PRINTM(ERROR, "uap_bss_stop:Hardware is not ready!\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); ++ if (!skb) { ++ PRINTM(ERROR, "No free skb\n"); ++ ret = -ENOMEM; ++ goto done; ++ } ++ CmdSize = sizeof(HostCmd_DS_GEN); ++ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); ++ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_BSS_STOP); ++ cmd->Size = uap_cpu_to_le16(CmdSize); ++ skb_put(skb, CmdSize + INTF_HEADER_LEN); ++ PRINTM(CMND, "APCMD_BSS_STOP\n"); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { ++ PRINTM(ERROR, "Fail to process cmd BSS_STOP\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/******************************************************** ++ Global Functions ++********************************************************/ ++/** ++ * @brief This function send soft_reset command to firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS on success, otherwise failure code ++ */ ++int ++uap_soft_reset(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u32 CmdSize; ++ HostCmd_DS_GEN *cmd; ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb; ++ ENTER(); ++ ret = uap_bss_stop(priv); ++ if (ret != UAP_STATUS_SUCCESS) ++ goto done; ++ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); ++ if (!skb) { ++ PRINTM(ERROR, "No free skb\n"); ++ ret = -ENOMEM; ++ goto done; ++ } ++ CmdSize = sizeof(HostCmd_DS_GEN); ++ cmd = (HostCmd_DS_GEN *) (skb->data + INTF_HEADER_LEN); ++ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SOFT_RESET); ++ cmd->Size = uap_cpu_to_le16(CmdSize); ++ skb_put(skb, CmdSize + INTF_HEADER_LEN); ++ PRINTM(CMND, "APCMD_SOFT_RESET\n"); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORSEND)) { ++ PRINTM(ERROR, "Fail to process cmd SOFT_RESET\n"); ++ ret = -EFAULT; ++ goto done; ++ } ++ Adapter->SurpriseRemoved = TRUE; ++ /* delay to allow hardware complete reset */ ++ os_sched_timeout(5); ++ if (priv->MediaConnected == TRUE) { ++ os_stop_queue(priv); ++ os_carrier_off(priv); ++ priv->MediaConnected = FALSE; ++ } ++ Adapter->CmdSize = 0; ++ Adapter->CmdWaitQWoken = TRUE; ++ wake_up_interruptible(&Adapter->cmdwait_q); ++ skb_queue_purge(&priv->adapter->tx_queue); ++ skb_queue_purge(&priv->adapter->cmd_queue); ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function processes received packet and forwards it ++ * to kernel/upper layer ++ * ++ * @param priv A pointer to uap_private ++ * @param skb A pointer to skb which includes the received packet ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++uap_process_rx_packet(uap_private * priv, struct sk_buff *skb) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ RxPD *pRxPD; ++ ENTER(); ++ priv->adapter->ps_state = PS_STATE_AWAKE; ++ pRxPD = (RxPD *) skb->data; ++ endian_convert_RxPD(pRxPD); ++ DBG_HEXDUMP(DAT_D, "Rx", skb->data, MIN(skb->len, DATA_DUMP_LEN)); ++ skb_pull(skb, pRxPD->RxPktOffset); ++ priv->stats.rx_packets++; ++ priv->stats.rx_bytes += skb->len; ++ os_upload_rx_packet(priv, skb); ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function opens the network device ++ * ++ * @param dev A pointer to net_device structure ++ * @return UAP_STATUS_SUCCESS ++ */ ++static int ++uap_open(struct net_device *dev) ++{ ++ uap_private *priv = (uap_private *) (uap_private *) netdev_priv(dev); ++ uap_adapter *Adapter = priv->adapter; ++ int i = 0; ++ ++ ENTER(); ++ ++ /* On some systems the device open handler will be called before HW ready. */ ++ /* Use the following flag check and wait function to work around the issue. */ ++ while ((Adapter->HardwareStatus != HWReady) && ++ (i < MAX_WAIT_DEVICE_READY_COUNT)) { ++ i++; ++ os_sched_timeout(100); ++ } ++ if (i >= MAX_WAIT_DEVICE_READY_COUNT) { ++ PRINTM(FATAL, "HW not ready, uap_open() return failure\n"); ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ ++ if (MODULE_GET == 0) ++ return UAP_STATUS_FAILURE; ++ ++ priv->open = TRUE; ++ if (priv->MediaConnected == TRUE) { ++ os_carrier_on(priv); ++ os_start_queue(priv); ++ } else { ++ os_stop_queue(priv); ++ os_carrier_off(priv); ++ } ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++ ++/** ++ * @brief This function closes the network device ++ * ++ * @param dev A pointer to net_device structure ++ * @return UAP_STATUS_SUCCESS ++ */ ++static int ++uap_close(struct net_device *dev) ++{ ++ uap_private *priv = (uap_private *) netdev_priv(dev); ++ ++ ENTER(); ++ skb_queue_purge(&priv->adapter->tx_queue); ++ os_stop_queue(priv); ++ os_carrier_off(priv); ++ ++ MODULE_PUT; ++ priv->open = FALSE; ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++ ++/** ++ * @brief This function returns the network statistics ++ * ++ * @param dev A pointer to uap_private structure ++ * @return A pointer to net_device_stats structure ++ */ ++static struct net_device_stats * ++uap_get_stats(struct net_device *dev) ++{ ++ uap_private *priv = (uap_private *) netdev_priv(dev); ++ ++ return &priv->stats; ++} ++ ++/** ++ * @brief This function sets the MAC address to firmware. ++ * ++ * @param dev A pointer to uap_private structure ++ * @param addr MAC address to set ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++uap_set_mac_address(struct net_device *dev, void *addr) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ uap_private *priv = (uap_private *) netdev_priv(dev); ++ struct sockaddr *pHwAddr = (struct sockaddr *) addr; ++ u32 CmdSize; ++ HostCmd_DS_COMMAND *cmd; ++ MrvlIEtypes_MacAddr_t *pMacAddrTlv; ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb; ++ ++ ENTER(); ++ ++ /* Dump MAC address */ ++ DBG_HEXDUMP(CMD_D, "Original MAC addr", dev->dev_addr, ETH_ALEN); ++ DBG_HEXDUMP(CMD_D, "New MAC addr", pHwAddr->sa_data, ETH_ALEN); ++ if (priv->open && (priv->MediaConnected == TRUE)) { ++ os_carrier_on(priv); ++ os_start_queue(priv); ++ } ++ skb = dev_alloc_skb(MRVDRV_SIZE_OF_CMD_BUFFER); ++ if (!skb) { ++ PRINTM(ERROR, "No free skb\n"); ++ LEAVE(); ++ return -ENOMEM; ++ } ++ CmdSize = ++ S_DS_GEN + sizeof(HostCmd_SYS_CONFIG) + sizeof(MrvlIEtypes_MacAddr_t); ++ cmd = (HostCmd_DS_COMMAND *) (skb->data + INTF_HEADER_LEN); ++ cmd->Command = uap_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); ++ cmd->Size = uap_cpu_to_le16(CmdSize); ++ cmd->params.sys_config.Action = uap_cpu_to_le16(ACTION_SET); ++ pMacAddrTlv = ++ (MrvlIEtypes_MacAddr_t *) ((u8 *) cmd + S_DS_GEN + ++ sizeof(HostCmd_SYS_CONFIG)); ++ pMacAddrTlv->Header.Type = uap_cpu_to_le16(MRVL_AP_MAC_ADDRESS_TLV_ID); ++ pMacAddrTlv->Header.Len = uap_cpu_to_le16(ETH_ALEN); ++ memcpy(pMacAddrTlv->ApMacAddr, pHwAddr->sa_data, ETH_ALEN); ++ skb_put(skb, CmdSize + INTF_HEADER_LEN); ++ PRINTM(CMND, "set_mac_address\n"); ++ if (UAP_STATUS_SUCCESS != ++ uap_process_cmd(priv, skb, HostCmd_OPTION_WAITFORRSP_TIMEOUT)) { ++ PRINTM(ERROR, "Fail to set mac address\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ if (!Adapter->CmdSize) { ++ PRINTM(ERROR, "Cmd Size is 0\n"); ++ LEAVE(); ++ return -EFAULT; ++ } ++ cmd = (HostCmd_DS_COMMAND *) Adapter->CmdBuf; ++ cmd->Result = uap_cpu_to_le16(cmd->Result); ++ if (cmd->Result != UAP_STATUS_SUCCESS) { ++ PRINTM(ERROR, "set mac addrress fail,cmd result=%x\n", cmd->Result); ++ ret = -EFAULT; ++ } else ++ memcpy(dev->dev_addr, pHwAddr->sa_data, ETH_ALEN); ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function handles the timeout of packet ++ * transmission ++ * ++ * @param dev A pointer to net_device structure ++ * @return n/a ++ */ ++static void ++uap_tx_timeout(struct net_device *dev) ++{ ++ uap_private *priv = (uap_private *) netdev_priv(dev); ++ ++ ENTER(); ++ ++ PRINTM(DATA, "Tx timeout\n"); ++ UpdateTransStart(dev); ++ priv->num_tx_timeout++; ++ priv->adapter->IntCounter++; ++ wake_up_interruptible(&priv->MainThread.waitQ); ++ ++ LEAVE(); ++} ++ ++/** ++ * @brief This function handles packet transmission ++ * ++ * @param skb A pointer to sk_buff structure ++ * @param dev A pointer to net_device structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++uap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ uap_private *priv = (uap_private *) netdev_priv(dev); ++ int ret = UAP_STATUS_SUCCESS; ++ ++ ENTER(); ++ PRINTM(DATA, "Data <= kernel\n"); ++ DBG_HEXDUMP(DAT_D, "Tx", skb->data, MIN(skb->len, DATA_DUMP_LEN)); ++ /* skb sanity check */ ++ if (!skb->len || (skb->len > MRVDRV_MAXIMUM_ETH_PACKET_SIZE)) { ++ PRINTM(ERROR, "Tx Error: Bad skb length %d : %d\n", skb->len, ++ MRVDRV_MAXIMUM_ETH_PACKET_SIZE); ++ priv->stats.tx_dropped++; ++ kfree(skb); ++ goto done; ++ } ++ skb_queue_tail(&priv->adapter->tx_queue, skb); ++ wake_up_interruptible(&priv->MainThread.waitQ); ++ if (skb_queue_len(&priv->adapter->tx_queue) > TX_HIGH_WATERMARK) { ++ UpdateTransStart(dev); ++ os_stop_queue(priv); ++ } ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief ioctl function - entry point ++ * ++ * @param dev A pointer to net_device structure ++ * @param req A pointer to ifreq structure ++ * @param cmd command ++ * @return UAP_STATUS_SUCCESS--success, otherwise fail ++ */ ++static int ++uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ ++ ENTER(); ++ ++ PRINTM(CMND, "uap_do_ioctl: ioctl cmd = 0x%x\n", cmd); ++ ++ switch (cmd) { ++ case UAPHOSTCMD: ++ ret = uap_hostcmd_ioctl(dev, req); ++ break; ++ case UAP_POWER_MODE: ++ ret = uap_power_mode_ioctl(dev, req); ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function handles events generated by firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @param payload A pointer to payload buffer ++ * @param len Length of the payload ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++uap_process_event(uap_private * priv, u8 * payload, uint len) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ uap_adapter *Adapter = priv->adapter; ++ struct sk_buff *skb = NULL; ++ struct nlmsghdr *nlh = NULL; ++ struct sock *sk = Adapter->nl_sk; ++ AP_Event *pEvent; ++ ++ ENTER(); ++ Adapter->ps_state = PS_STATE_AWAKE; ++ if (len > NL_MAX_PAYLOAD) { ++ PRINTM(ERROR, "event size is too big!!! len=%d\n", len); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ pEvent = (AP_Event *) payload; ++ PRINTM(CMND, "Event: %d\n", pEvent->EventId); ++ switch (pEvent->EventId) { ++ case MICRO_AP_EV_ID_BSS_START: ++ memcpy(priv->uap_dev.netdev->dev_addr, pEvent->MacAddr, ETH_ALEN); ++ DBG_HEXDUMP(CMD_D, "BSS MAC addr", priv->uap_dev.netdev->dev_addr, ++ ETH_ALEN); ++ break; ++ case MICRO_AP_EV_BSS_ACTIVE: ++ // carrier on ++ priv->MediaConnected = TRUE; ++ os_carrier_on(priv); ++ os_start_queue(priv); ++ break; ++ case MICRO_AP_EV_BSS_IDLE: ++ os_stop_queue(priv); ++ os_carrier_off(priv); ++ priv->MediaConnected = FALSE; ++ break; ++ case EVENT_PS_AWAKE: ++ PRINTM(CMND, "UAP: PS_AWAKE\n"); ++ Adapter->ps_state = PS_STATE_AWAKE; ++ Adapter->WakeupTries = 0; ++ break; ++ case EVENT_PS_SLEEP: ++ PRINTM(CMND, "UAP: PS_SLEEP\n"); ++ Adapter->ps_state = PS_STATE_PRE_SLEEP; ++ break; ++ default: ++ break; ++ } ++ if ((pEvent->EventId == EVENT_PS_AWAKE) || ++ (pEvent->EventId == EVENT_PS_SLEEP)) ++ goto done; ++ if (sk) { ++ /* Allocate skb */ ++ if (!(skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), GFP_ATOMIC))) { ++ PRINTM(ERROR, "Could not allocate skb for netlink.\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ nlh = (struct nlmsghdr *) skb->data; ++ nlh->nlmsg_len = NLMSG_SPACE(len); ++ ++ /* From kernel */ ++ nlh->nlmsg_pid = 0; ++ nlh->nlmsg_flags = 0; ++ ++ /* Data */ ++ skb_put(skb, nlh->nlmsg_len); ++ memcpy(NLMSG_DATA(nlh), payload, len); ++ ++ /* From Kernel */ ++ NETLINK_CB(skb).portid = 0; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ /* Multicast message */ ++ NETLINK_CB(skb).dst_pid = 0; ++#endif ++ ++ /* Multicast group number */ ++ NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP; ++ ++ /* Send message */ ++ netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, GFP_KERNEL); ++ ++ ret = UAP_STATUS_SUCCESS; ++ } else { ++ PRINTM(ERROR, "Could not send event through NETLINK. Link down.\n"); ++ ret = UAP_STATUS_FAILURE; ++ } ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @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 priv A pointer to uap_private structure ++ * @return n/a ++ */ ++void ++uap_interrupt(uap_private * priv) ++{ ++ ENTER(); ++ priv->adapter->IntCounter++; ++ priv->adapter->WakeupTries = 0; ++ PRINTM(INFO, "*\n"); ++ wake_up_interruptible(&priv->MainThread.waitQ); ++ ++ LEAVE(); ++ ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29) ++/** Network device handlers */ ++static const struct net_device_ops uap_netdev_ops = { ++ .ndo_open = uap_open, ++ .ndo_start_xmit = uap_hard_start_xmit, ++ .ndo_stop = uap_close, ++ .ndo_do_ioctl = uap_do_ioctl, ++ .ndo_set_mac_address = uap_set_mac_address, ++ .ndo_tx_timeout = uap_tx_timeout, ++ .ndo_get_stats = uap_get_stats, ++}; ++#endif ++ ++/** ++ * @brief This function adds the card. it will probe the ++ * card, allocate the uap_priv and initialize the device. ++ * ++ * @param card A pointer to card ++ * @return A pointer to uap_private structure ++ */ ++uap_private * ++uap_add_card(void *card) ++{ ++ struct net_device *dev = NULL; ++ uap_private *priv = NULL; ++ ++ ENTER(); ++ ++ if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) ++ goto exit_sem_err; ++ ++ /* Allocate an Ethernet device */ ++ if (!(dev = alloc_etherdev(sizeof(uap_private)))) { ++ PRINTM(FATAL, "Init ethernet device failed!\n"); ++ goto error; ++ } ++ priv = (uap_private *) netdev_priv(dev); ++ ++ /* Allocate name */ ++ if (dev_alloc_name(dev, "uap%d") < 0) { ++ PRINTM(ERROR, "Could not allocate device name!\n"); ++ goto error; ++ } ++ ++ /* Allocate buffer for uap_adapter */ ++ if (!(priv->adapter = kmalloc(sizeof(uap_adapter), GFP_KERNEL))) { ++ PRINTM(FATAL, "Allocate buffer for uap_adapter failed!\n"); ++ goto error; ++ } ++ memset(priv->adapter, 0, sizeof(uap_adapter)); ++ ++ priv->uap_dev.netdev = dev; ++ priv->uap_dev.card = card; ++ priv->MediaConnected = FALSE; ++ uappriv = priv; ++ ((struct sdio_mmc_card *) card)->priv = priv; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) ++ SET_MODULE_OWNER(dev); ++#endif ++ ++ /* Setup the OS Interface to our functions */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) ++ dev->open = uap_open; ++ dev->stop = uap_close; ++ dev->hard_start_xmit = uap_hard_start_xmit; ++ dev->tx_timeout = uap_tx_timeout; ++ dev->get_stats = uap_get_stats; ++ dev->do_ioctl = uap_do_ioctl; ++ dev->set_mac_address = uap_set_mac_address; ++ dev->set_multicast_list = uap_set_multicast_list; ++#else ++ dev->netdev_ops = &uap_netdev_ops; ++#endif ++ dev->watchdog_timeo = MRVDRV_DEFAULT_WATCHDOG_TIMEOUT; ++ dev->hard_header_len += sizeof(TxPD) + INTF_HEADER_LEN; ++ dev->hard_header_len += HEADER_ALIGNMENT; ++#define NETIF_F_DYNALLOC 16 ++ dev->features |= NETIF_F_DYNALLOC; ++ dev->flags |= IFF_BROADCAST | IFF_MULTICAST; ++ ++ /* Init SW */ ++ if (uap_init_sw(priv)) { ++ PRINTM(FATAL, "Software Init Failed\n"); ++ goto error; ++ } ++ ++ PRINTM(INFO, "Starting kthread...\n"); ++ priv->MainThread.priv = priv; ++ spin_lock_init(&priv->driver_lock); ++ uap_create_thread(uap_service_main_thread, &priv->MainThread, ++ "uap_main_service"); ++ while (priv->MainThread.pid == 0) { ++ os_sched_timeout(2); ++ } ++ ++ /* Register the device */ ++ if (sbi_register_dev(priv) < 0) { ++ PRINTM(FATAL, "Failed to register uap device!\n"); ++ goto err_registerdev; ++ } ++#ifdef FW_DNLD_NEEDED ++ SET_NETDEV_DEV(dev, priv->hotplug_device); ++#endif ++ ++ /* Init FW and HW */ ++ if (uap_init_fw(priv)) { ++ PRINTM(FATAL, "Firmware Init Failed\n"); ++ goto err_init_fw; ++ } ++ ++ priv->uap_dev.cmd_sent = FALSE; ++ priv->uap_dev.data_sent = FALSE; ++ ++ /* Get mac address from firmware */ ++ if (uap_get_mac_address(priv)) { ++ PRINTM(FATAL, "Fail to get mac address\n"); ++ goto err_init_fw; ++ } ++ /* Register network device */ ++ if (register_netdev(dev)) { ++ printk(KERN_ERR "Cannot register network device!\n"); ++ goto err_init_fw; ++ } ++#ifdef CONFIG_PROC_FS ++ uap_proc_entry(priv, dev); ++ uap_debug_entry(priv, dev); ++#endif /* CPNFIG_PROC_FS */ ++ OS_REL_SEMAPHORE(&AddRemoveCardSem); ++ ++ LEAVE(); ++ return priv; ++ err_init_fw: ++ 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); ++ } ++ error: ++ if (dev) { ++ if (dev->reg_state == NETREG_REGISTERED) ++ unregister_netdev(dev); ++ if (priv->adapter) ++ uap_free_adapter(priv); ++ free_netdev(dev); ++ uappriv = NULL; ++ } ++ OS_REL_SEMAPHORE(&AddRemoveCardSem); ++ exit_sem_err: ++ LEAVE(); ++ return NULL; ++} ++ ++/** ++ * @brief This function removes the card. ++ * ++ * @param card A pointer to card ++ * @return UAP_STATUS_SUCCESS ++ */ ++int ++uap_remove_card(void *card) ++{ ++ uap_private *priv = uappriv; ++ uap_adapter *Adapter; ++ struct net_device *dev; ++ ++ ENTER(); ++ ++ if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) ++ goto exit_sem_err; ++ ++ if (!priv || !(Adapter = priv->adapter)) { ++ goto exit_remove; ++ } ++ Adapter->SurpriseRemoved = TRUE; ++ if (Adapter->cmd_pending == TRUE) { ++ /* Wake up cmd Q */ ++ Adapter->CmdWaitQWoken = TRUE; ++ wake_up_interruptible(&Adapter->cmdwait_q); ++ } ++ dev = priv->uap_dev.netdev; ++ if (priv->MediaConnected == TRUE) { ++ os_stop_queue(priv); ++ os_carrier_off(priv); ++ priv->MediaConnected = FALSE; ++ } ++ Adapter->CmdSize = 0; ++ Adapter->CmdWaitQWoken = TRUE; ++ wake_up_interruptible(&Adapter->cmdwait_q); ++ skb_queue_purge(&priv->adapter->tx_queue); ++ skb_queue_purge(&priv->adapter->cmd_queue); ++ ++ /* Disable interrupts on the card */ ++ sbi_disable_host_int(priv); ++ PRINTM(INFO, "netdev_finish_unregister: %s%s.\n", dev->name, ++ (dev->features & NETIF_F_DYNALLOC) ? "" : ", old style"); ++ unregister_netdev(dev); ++ PRINTM(INFO, "Unregister finish\n"); ++ wake_up_interruptible(&priv->MainThread.waitQ); ++ while (priv->MainThread.pid) { ++ os_sched_timeout(1); ++ } ++ ++ if ((Adapter->nl_sk) && ((Adapter->nl_sk)->sk_socket)) { ++ sock_release((Adapter->nl_sk)->sk_socket); ++ Adapter->nl_sk = NULL; ++ } ++#ifdef CONFIG_PROC_FS ++ uap_debug_remove(priv); ++ uap_proc_remove(priv); ++#endif ++ sbi_unregister_dev(priv); ++ PRINTM(INFO, "Free Adapter\n"); ++ uap_free_adapter(priv); ++ priv->uap_dev.netdev = NULL; ++ free_netdev(dev); ++ uappriv = NULL; ++ ++ exit_remove: ++ OS_REL_SEMAPHORE(&AddRemoveCardSem); ++ exit_sem_err: ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++ ++/** ++ * @brief This function initializes module. ++ * ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int __init ++uap_init_module(void) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ ENTER(); ++ ++ OS_INIT_SEMAPHORE(&AddRemoveCardSem); ++ ret = sbi_register(); ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function cleans module ++ * ++ * @return n/a ++ */ ++static void __exit ++uap_cleanup_module(void) ++{ ++ ENTER(); ++ ++ if (OS_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) ++ goto exit_sem_err; ++ ++ if ((uappriv) && (uappriv->adapter)) { ++ uap_func_shutdown(uappriv); ++ } ++ OS_REL_SEMAPHORE(&AddRemoveCardSem); ++ exit_sem_err: ++ sbi_unregister(); ++ LEAVE(); ++} ++ ++module_init(uap_init_module); ++module_exit(uap_cleanup_module); ++module_param(helper_name, charp, 0); ++MODULE_PARM_DESC(helper_name, "Helper name"); ++module_param(fw_name, charp, 0); ++MODULE_PARM_DESC(fw_name, "Firmware name"); ++ ++MODULE_DESCRIPTION("M-UAP Driver"); ++MODULE_AUTHOR("Marvell International Ltd."); ++MODULE_VERSION(DRIVER_VERSION); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/net/wireless/libertas_uap/uap_proc.c b/drivers/net/wireless/libertas_uap/uap_proc.c +new file mode 100644 +index 0000000..d673f56 +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_proc.c +@@ -0,0 +1,258 @@ ++/** @file uap_proc.c ++ * @brief This file contains functions for proc file. ++ * ++ * Copyright (C) 2008-2009, 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. ++ * ++ */ ++#ifdef CONFIG_PROC_FS ++#include <linux/proc_fs.h> ++#include <linux/seq_file.h> ++#include "uap_headers.h" ++ ++/** /proc directory 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 ++ ++/******************************************************** ++ Local Variables ++********************************************************/ ++ ++/******************************************************** ++ Global Variables ++********************************************************/ ++ ++/******************************************************** ++ Local Functions ++********************************************************/ ++ ++static int uap_info_proc_show(struct seq_file *s, void *data) { ++ int i; ++ struct net_device *netdev = (struct net_device*)s->private; ++ struct netdev_hw_addr *ha; ++ uap_private *priv = (uap_private *) netdev_priv(netdev); ++ ++ seq_printf(s, "driver_name = " ""uap"\n"); ++ seq_printf(s, "driver_version = %s-(FP%s)", DRIVER_VERSION, FPNUM); ++ seq_printf(s, "\nInterfaceName="%s"\n", netdev->name); ++ ++ seq_printf(s, "State="%s"\n", ++ ((priv->MediaConnected == ++ FALSE) ? "Disconnected" : "Connected")); ++ seq_printf(s, "MACAddress="%02x:%02x:%02x:%02x:%02x:%02x"\n", ++ netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], ++ netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); ++ i = 0; ++ netdev_for_each_mc_addr(ha, netdev) { ++ ++i; ++ } ++ seq_printf(s, "MCCount="%d"\n", i); ++ ++ /* ++ * Put out the multicast list ++ */ ++ i = 0; ++ netdev_for_each_mc_addr(ha, netdev) { ++ seq_printf(s, ++ "MCAddr[%d]="%02x:%02x:%02x:%02x:%02x:%02x"\n", ++ i++, ++ ha->addr[0], ha->addr[1], ++ ha->addr[2], ha->addr[3], ++ ha->addr[4], ha->addr[5]); ++ } ++ ++ seq_printf(s, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); ++ seq_printf(s, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); ++ seq_printf(s, "num_tx_pkts = %lu\n", priv->stats.tx_packets); ++ seq_printf(s, "num_rx_pkts = %lu\n", priv->stats.rx_packets); ++ seq_printf(s, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); ++ seq_printf(s, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); ++ seq_printf(s, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); ++ seq_printf(s, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); ++ seq_printf(s, "num_tx_timeout = %u\n", priv->num_tx_timeout); ++ seq_printf(s, "carrier %s\n", ++ ((netif_carrier_ok(priv->uap_dev.netdev)) ? "on" : "off")); ++ seq_printf(s, "tx queue %s\n", ++ ((netif_queue_stopped(priv->uap_dev.netdev)) ? "stopped" : ++ "started")); ++ ++ return 0; ++} ++ ++static int uap_info_proc_open(struct inode *inode, struct file *file) { ++ return single_open(file, uap_info_proc_show, PDE_DATA(inode)); ++} ++ ++static int uap_hwstatus_proc_show(struct seq_file *s, void *data) { ++ struct net_device *netdev = (struct net_device*)s->private; ++ uap_private *priv = (uap_private *) netdev_priv(netdev); ++ ++ MODULE_GET; ++ seq_printf(s, "%d\n", priv->adapter->HardwareStatus); ++ MODULE_PUT; ++ ++ return 0; ++} ++ ++static int uap_hwstatus_proc_open(struct inode *inode, struct file *file) { ++ return single_open(file, uap_hwstatus_proc_show, PDE_DATA(inode)); ++} ++ ++static ssize_t uap_hwstatus_proc_write(struct file *file, const char __user *buffer, size_t count, loff_t *data) { ++ struct net_device *netdev = (struct net_device *)PDE_DATA(file_inode(file)); ++ uap_private *priv = (uap_private *) netdev_priv(netdev); ++ ++ int hwstatus; ++ char value[10]; ++ ++ if (count > sizeof(value)) ++ return count; ++ ++ if (copy_from_user(&value, buffer, count)) ++ return -EFAULT; ++ ++ hwstatus = string_to_number(value); ++ switch (hwstatus) { ++ case HWReset: ++ PRINTM(MSG, "reset hw\n"); ++ uap_soft_reset(priv); ++ priv->adapter->HardwareStatus = HWReset; ++ break; ++ default: ++ break; ++ } ++ ++ MODULE_PUT; ++ return count; ++} ++ ++static const struct file_operations uap_info_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = uap_info_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static const struct file_operations uap_hwstatus_proc_fops = { ++ .owner = THIS_MODULE, ++ .open = uap_hwstatus_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .write = uap_hwstatus_proc_write, ++}; ++ ++/******************************************************** ++ Global Functions ++********************************************************/ ++/** ++ * @brief create uap proc file ++ * ++ * @param priv pointer uap_private ++ * @param dev pointer net_device ++ * @return N/A ++ */ ++void ++uap_proc_entry(uap_private * priv, struct net_device *dev) ++{ ++ PRINTM(INFO, "Creating Proc Interface\n"); ++ /* Check if uap directory already exists */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) ++ struct proc_dir_entry *r = PROC_DIR; ++ ++ for (r = r->subdir; r; r = r->next) { ++ if (r->namelen && !strcmp("uap", r->name)) { ++ /* Directory exists */ ++ PRINTM(WARN, "proc directory already exists!\n"); ++ priv->proc_uap = r; ++ break; ++ } ++ } ++#endif ++ if (!priv->proc_uap) { ++ priv->proc_uap = proc_mkdir("uap", PROC_DIR); ++ if (!priv->proc_uap) ++ return; ++ } ++ priv->proc_entry = proc_mkdir(dev->name, priv->proc_uap); ++ ++ if (priv->proc_entry) { ++ proc_create_data("info", 0644, priv->proc_entry, &uap_info_proc_fops, dev); ++ proc_create_data("hwinfo", 0644, priv->proc_entry, &uap_hwstatus_proc_fops, dev); ++ } ++} ++ ++/** ++ * @brief remove proc file ++ * ++ * @param priv pointer uap_private ++ * @return N/A ++ */ ++void ++uap_proc_remove(uap_private * priv) ++{ ++ if (priv->proc_uap) { ++ if (priv->proc_entry) { ++ remove_proc_entry("info", priv->proc_entry); ++ remove_proc_entry("hwstatus", priv->proc_entry); ++ } ++ remove_proc_entry(priv->uap_dev.netdev->name, priv->proc_uap); ++ } ++} ++ ++/** ++ * @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); ++} ++ ++#endif +diff --git a/drivers/net/wireless/libertas_uap/uap_sdio_mmc.c b/drivers/net/wireless/libertas_uap/uap_sdio_mmc.c +new file mode 100644 +index 0000000..86f55b5 +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_sdio_mmc.c +@@ -0,0 +1,1428 @@ ++/** @file uap_sdio_mmc.c ++ * @brief This file contains SDIO IF (interface) module ++ * related functions. ++ * ++ * Copyright (C) 2007-2009, 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: ++****************************************************/ ++ ++#include "uap_sdio_mmc.h" ++ ++#include <linux/firmware.h> ++ ++/** define SDIO block size */ ++/* We support up to 480-byte block size due to FW buffer limitation. */ ++#define SD_BLOCK_SIZE 256 ++ ++/** define allocated buffer size */ ++#define ALLOC_BUF_SIZE (((MAX(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, \ ++ MRVDRV_SIZE_OF_CMD_BUFFER) + INTF_HEADER_LEN \ ++ + SD_BLOCK_SIZE - 1) / SD_BLOCK_SIZE) * SD_BLOCK_SIZE) ++ ++/** Max retry number of CMD53 write */ ++#define MAX_WRITE_IOMEM_RETRY 2 ++ ++/******************************************************** ++ Local Variables ++********************************************************/ ++ ++/** SDIO Rx unit */ ++static u8 sdio_rx_unit = 0; ++ ++/**Interrupt status */ ++static u8 sd_ireg = 0; ++/******************************************************** ++ Global Variables ++********************************************************/ ++extern u8 *helper_name; ++extern u8 *fw_name; ++/** Default helper name */ ++#define DEFAULT_HELPER_NAME "mrvl/helper_sd.bin" ++/** Default firmware name */ ++#define DEFAULT_FW_NAME "mrvl/sd8688_ap.bin" ++ ++/******************************************************** ++ Local Functions ++********************************************************/ ++/** ++ * @brief This function reads the IO register. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param reg register to be read ++ * @param dat A pointer to variable that keeps returned value ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++sbi_read_ioreg(uap_private * priv, u32 reg, u8 * dat) ++{ ++ struct sdio_mmc_card *card; ++ int ret = UAP_STATUS_FAILURE; ++ ++ ENTER(); ++ ++ card = priv->uap_dev.card; ++ if (!card || !card->func) { ++ PRINTM(ERROR, "sbi_read_ioreg(): card or function is NULL!\n"); ++ goto done; ++ } ++ ++ *dat = sdio_readb(card->func, reg, &ret); ++ if (ret) { ++ PRINTM(ERROR, "sbi_read_ioreg(): sdio_readb failed! ret=%d\n", ret); ++ goto done; ++ } ++ ++ PRINTM(INFO, "sbi_read_ioreg() priv=%p func=%d reg=%#x dat=%#x\n", priv, ++ card->func->num, reg, *dat); ++ ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function writes the IO register. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param reg register to be written ++ * @param dat the value to be written ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++sbi_write_ioreg(uap_private * priv, u32 reg, u8 dat) ++{ ++ struct sdio_mmc_card *card; ++ int ret = UAP_STATUS_FAILURE; ++ ++ ENTER(); ++ ++ card = priv->uap_dev.card; ++ if (!card || !card->func) { ++ PRINTM(ERROR, "sbi_write_ioreg(): card or function is NULL!\n"); ++ goto done; ++ } ++ ++ PRINTM(INFO, "sbi_write_ioreg() priv=%p func=%d reg=%#x dat=%#x\n", priv, ++ card->func->num, reg, dat); ++ ++ sdio_writeb(card->func, dat, reg, &ret); ++ if (ret) { ++ PRINTM(ERROR, "sbi_write_ioreg(): sdio_readb failed! ret=%d\n", ret); ++ goto done; ++ } ++ ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function get rx_unit value ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++sd_get_rx_unit(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u8 reg; ++ ++ ENTER(); ++ ++ ret = sbi_read_ioreg(priv, CARD_RX_UNIT_REG, ®); ++ if (ret == UAP_STATUS_SUCCESS) ++ sdio_rx_unit = reg; ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function reads rx length ++ * ++ * @param priv A pointer to uap_private structure ++ * @param dat A pointer to keep returned data ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++sd_read_rx_len(uap_private * priv, u16 * dat) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u8 reg; ++ ++ ENTER(); ++ ++ ret = sbi_read_ioreg(priv, CARD_RX_LEN_REG, ®); ++ if (ret == UAP_STATUS_SUCCESS) ++ *dat = (u16) reg << sdio_rx_unit; ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function reads fw status registers ++ * ++ * @param priv A pointer to uap_private structure ++ * @param dat A pointer to keep returned data ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++sd_read_firmware_status(uap_private * priv, u16 * dat) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u8 fws0; ++ u8 fws1; ++ ++ ENTER(); ++ ++ ret = sbi_read_ioreg(priv, CARD_FW_STATUS0_REG, &fws0); ++ if (ret < 0) { ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ ++ ret = sbi_read_ioreg(priv, CARD_FW_STATUS1_REG, &fws1); ++ if (ret < 0) { ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ ++ *dat = (((u16) fws1) << 8) | fws0; ++ ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++ ++/** ++ * @brief This function polls the card status register. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param bits the bit mask ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++mv_sdio_poll_card_status(uap_private * priv, u8 bits) ++{ ++ int tries; ++ u8 cs; ++ ++ ENTER(); ++ ++ for (tries = 0; tries < MAX_POLL_TRIES; tries++) { ++ if (sbi_read_ioreg(priv, CARD_STATUS_REG, &cs) < 0) ++ break; ++ else if ((cs & bits) == bits) { ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++ } ++ udelay(10); ++ } ++ ++ PRINTM(WARN, "mv_sdio_poll_card_status failed, tries = %d\n", tries); ++ ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++} ++ ++/** ++ * @brief This function set the sdio bus width. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param mode 1--1 bit mode, 4--4 bit mode ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++#if 0 ++static int ++sdio_set_bus_width(uap_private * priv, u8 mode) ++{ ++ ENTER(); ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++#endif ++ ++/** ++ * @brief This function reads data from the card. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++sd_card_to_host(uap_private * priv) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u16 buf_len = 0; ++ int buf_block_len; ++ int blksz; ++ struct sk_buff *skb = NULL; ++ u16 type; ++ u8 *payload = NULL; ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "card or function is NULL!\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto exit; ++ } ++ ++ /* Read the length of data to be transferred */ ++ ret = sd_read_rx_len(priv, &buf_len); ++ if (ret < 0) { ++ PRINTM(ERROR, "card_to_host, read scratch reg failed\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto exit; ++ } ++ ++ /* Allocate buffer */ ++ blksz = SD_BLOCK_SIZE; ++ buf_block_len = (buf_len + blksz - 1) / blksz; ++ if (buf_len <= INTF_HEADER_LEN || (buf_block_len * blksz) > ALLOC_BUF_SIZE) { ++ PRINTM(ERROR, "card_to_host, invalid packet length: %d\n", buf_len); ++ ret = UAP_STATUS_FAILURE; ++ goto exit; ++ } ++#ifdef PXA3XX_DMA_ALIGN ++ skb = dev_alloc_skb(buf_block_len * blksz + PXA3XX_DMA_ALIGNMENT); ++#else ++ skb = dev_alloc_skb(buf_block_len * blksz); ++#endif ++ if (skb == NULL) { ++ PRINTM(WARN, "No free skb\n"); ++ goto exit; ++ } ++#ifdef PXA3XX_DMA_ALIGN ++ if ((u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)) { ++ skb_put(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); ++ skb_pull(skb, (u32) skb->data & (PXA3XX_DMA_ALIGNMENT - 1)); ++ } ++#endif /* PXA3XX_DMA_ALIGN */ ++ ++ payload = skb->tail; ++ ret = sdio_readsb(card->func, payload, priv->uap_dev.ioport, ++ buf_block_len * blksz); ++ if (ret < 0) { ++ PRINTM(ERROR, "card_to_host, read iomem failed: %d\n", ret); ++ ret = UAP_STATUS_FAILURE; ++ goto exit; ++ } ++ HEXDUMP("SDIO Blk Rd", payload, blksz * buf_block_len); ++ /* ++ * This is SDIO specific header ++ * u16 length, ++ * u16 type (MV_TYPE_DAT = 0, MV_TYPE_CMD = 1, MV_TYPE_EVENT = 3) ++ */ ++ buf_len = uap_le16_to_cpu(*(u16 *) & payload[0]); ++ type = uap_le16_to_cpu(*(u16 *) & payload[2]); ++ switch (type) { ++ case MV_TYPE_EVENT: ++ skb_put(skb, buf_len); ++ skb_pull(skb, INTF_HEADER_LEN); ++ uap_process_event(priv, skb->data, skb->len); ++ kfree_skb(skb); ++ skb = NULL; ++ break; ++ case MV_TYPE_CMD: ++ skb_put(skb, buf_len); ++ skb_pull(skb, INTF_HEADER_LEN); ++ priv->adapter->cmd_pending = FALSE; ++ if (priv->adapter->cmd_wait_option == ++ HostCmd_OPTION_WAITFORRSP_SLEEPCONFIRM) { ++ priv->adapter->cmd_wait_option = FALSE; ++ uap_process_sleep_confirm_resp(priv, skb->data, skb->len); ++ } else if (priv->adapter->cmd_wait_option) { ++ memcpy(priv->adapter->CmdBuf, skb->data, skb->len); ++ priv->adapter->CmdSize = skb->len; ++ priv->adapter->cmd_wait_option = FALSE; ++ priv->adapter->CmdWaitQWoken = TRUE; ++ wake_up_interruptible(&priv->adapter->cmdwait_q); ++ } ++ kfree_skb(skb); ++ skb = NULL; ++ break; ++ case MV_TYPE_DAT: ++ skb_put(skb, buf_len); ++ skb_pull(skb, INTF_HEADER_LEN); ++ uap_process_rx_packet(priv, skb); ++ break; ++ default: ++ priv->stats.rx_errors++; ++ priv->stats.rx_dropped++; ++ /* Driver specified event and command resp should be handle here */ ++ PRINTM(INFO, "Unknown PKT type:%d\n", type); ++ kfree_skb(skb); ++ skb = NULL; ++ break; ++ } ++ exit: ++ if (ret) { ++ if (skb) ++ kfree_skb(skb); ++ } ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function enables the host interrupts mask ++ * ++ * @param priv A pointer to uap_private structure ++ * @param mask the interrupt mask ++ * @return UAP_STATUS_SUCCESS ++ */ ++static int ++enable_host_int_mask(uap_private * priv, u8 mask) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ ++ ENTER(); ++ ++ /* Simply write the mask to the register */ ++ ret = sbi_write_ioreg(priv, HOST_INT_MASK_REG, mask); ++ ++ if (ret) { ++ PRINTM(WARN, "Unable to enable the host interrupt!\n"); ++ ret = UAP_STATUS_FAILURE; ++ } ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** @brief This function disables the host interrupts mask. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param mask the interrupt mask ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++disable_host_int_mask(uap_private * priv, u8 mask) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u8 host_int_mask; ++ ++ ENTER(); ++ ++ /* Read back the host_int_mask register */ ++ ret = sbi_read_ioreg(priv, HOST_INT_MASK_REG, &host_int_mask); ++ if (ret) { ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ ++ /* Update with the mask and write back to the register */ ++ host_int_mask &= ~mask; ++ ret = sbi_write_ioreg(priv, HOST_INT_MASK_REG, host_int_mask); ++ if (ret < 0) { ++ PRINTM(WARN, "Unable to diable the host interrupt!\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/******************************************************** ++ Global Functions ++********************************************************/ ++ ++/** ++ * @brief This function handles the interrupt. ++ * ++ * @param func A pointer to sdio_func structure. ++ * @return n/a ++ */ ++static void ++sbi_interrupt(struct sdio_func *func) ++{ ++ struct sdio_mmc_card *card; ++ uap_private *priv; ++ u8 ireg = 0; ++ int ret = UAP_STATUS_SUCCESS; ++ ++ ENTER(); ++ ++ card = sdio_get_drvdata(func); ++ if (!card || !card->priv) { ++ PRINTM(MSG, "%s: sbi_interrupt(%p) card or priv is NULL, card=%p\n", ++ __FUNCTION__, func, card); ++ LEAVE(); ++ return; ++ } ++ priv = card->priv; ++#ifdef FW_WAKEUP_TIME ++ if ((priv->adapter->wt_pwrup_sending != 0L) && ++ (priv->adapter->wt_int == 0L)) ++ priv->adapter->wt_int = get_utimeofday(); ++#endif ++ ++ ireg = sdio_readb(card->func, HOST_INTSTATUS_REG, &ret); ++ if (ret) { ++ PRINTM(WARN, "sdio_read_ioreg: read int status register failed\n"); ++ 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(INFO, "sdio_ireg = 0x%x\n", ireg); ++ sdio_writeb(card->func, ++ ~(ireg) & (DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS), ++ HOST_INTSTATUS_REG, &ret); ++ if (ret) { ++ PRINTM(WARN, ++ "sdio_write_ioreg: clear int status register failed\n"); ++ goto done; ++ } ++ } ++ OS_INT_DISABLE; ++ sd_ireg |= ireg; ++ OS_INT_RESTORE; ++ ++ uap_interrupt(priv); ++ done: ++ LEAVE(); ++} ++ ++/** ++ * @brief This function probe the card ++ * ++ * @param func A pointer to sdio_func structure ++ * @param id A pointer to structure sd_device_id ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++static int ++uap_probe(struct sdio_func *func, const struct sdio_device_id *id) ++{ ++ int ret = UAP_STATUS_FAILURE; ++ struct sdio_mmc_card *card = NULL; ++ ++ ENTER(); ++ ++ PRINTM(MSG, "%s: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", ++ __FUNCTION__, func->vendor, func->device, func->class, func->num); ++ ++ card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); ++ if (!card) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ card->func = func; ++ ++ if (!uap_add_card(card)) { ++ PRINTM(ERROR, "%s: uap_add_callback failed\n", __FUNCTION__); ++ kfree(card); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ ++ ret = UAP_STATUS_SUCCESS; ++ ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function removes the card ++ * ++ * @param func A pointer to sdio_func structure ++ * @return N/A ++ */ ++static void ++uap_remove(struct sdio_func *func) ++{ ++ struct sdio_mmc_card *card; ++ ++ ENTER(); ++ ++ if (func) { ++ card = sdio_get_drvdata(func); ++ if (card) { ++ uap_remove_card(card); ++ kfree(card); ++ } ++ } ++ ++ LEAVE(); ++} ++ ++#ifdef CONFIG_PM ++/** ++ * @brief This function handles client driver suspend ++ * ++ * @param func A pointer to sdio_func structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++uap_suspend(struct sdio_func *func) ++{ ++ ENTER(); ++ LEAVE(); ++ return 0; ++} ++ ++/** ++ * @brief This function handles client driver resume ++ * ++ * @param func A pointer to sdio_func structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++uap_resume(struct sdio_func *func) ++{ ++ ENTER(); ++ LEAVE(); ++ return 0; ++} ++#endif ++ ++/** Device ID for SD8688 */ ++#define SD_DEVICE_ID_8688_UAP 0x9104 ++/** UAP IDs */ ++static const struct sdio_device_id uap_ids[] = { ++ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SD_DEVICE_ID_8688_UAP)}, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(sdio, uap_ids); ++ ++static struct sdio_driver uap_sdio = { ++ .name = "uap_sdio", ++ .id_table = uap_ids, ++ .probe = uap_probe, ++ .remove = uap_remove, ++#ifdef CONFIG_PM ++/* .suspend = uap_suspend, */ ++/* .resume = uap_resume, */ ++#endif ++ ++}; ++ ++/** ++ * @brief This function registers the IF module in bus driver. ++ * ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int __init ++sbi_register() ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ ++ ENTER(); ++ ++ /* SDIO Driver Registration */ ++ if (sdio_register_driver(&uap_sdio) != 0) { ++ PRINTM(FATAL, "SDIO Driver Registration Failed \n"); ++ ret = UAP_STATUS_FAILURE; ++ } ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function de-registers the IF module in bus driver. ++ * ++ * @return n/a ++ */ ++void __exit ++sbi_unregister(void) ++{ ++ ENTER(); ++ ++ /* SDIO Driver Unregistration */ ++ sdio_unregister_driver(&uap_sdio); ++ ++ LEAVE(); ++} ++ ++/** ++ * @brief This function checks the interrupt status and handle it accordingly. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param ireg A pointer to variable that keeps returned value ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_get_int_status(uap_private * priv, u8 * ireg) ++{ ++ int ret = UAP_STATUS_SUCCESS; ++ u8 sdio_ireg = 0; ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ ++ ENTER(); ++ ++ *ireg = 0; ++ OS_INT_DISABLE; ++ sdio_ireg = sd_ireg; ++ sd_ireg = 0; ++ OS_INT_RESTORE; ++ ++ sdio_claim_host(card->func); ++ ++ if (sdio_ireg & DN_LD_HOST_INT_STATUS) { /* tx_done INT */ ++ if (!priv->uap_dev.cmd_sent) { /* tx_done already received */ ++ PRINTM(INFO, ++ "warning: tx_done already received: tx_dnld_rdy=0x%x int status=0x%x\n", ++ priv->uap_dev.cmd_sent, sdio_ireg); ++ } else { ++ priv->uap_dev.cmd_sent = FALSE; ++ priv->uap_dev.data_sent = FALSE; ++ if ( (priv->uap_dev.netdev->reg_state == NETREG_REGISTERED) && (skb_queue_len(&priv->adapter->tx_queue) < TX_LOW_WATERMARK)) { ++ os_start_queue(priv); ++ } ++ } ++ } ++ if (sdio_ireg & UP_LD_HOST_INT_STATUS) { ++ sd_card_to_host(priv); ++ } ++ ++ *ireg = sdio_ireg; ++ ret = UAP_STATUS_SUCCESS; ++ sdio_release_host(card->func); ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function disables the host interrupts. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_disable_host_int(uap_private * priv) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ int ret; ++ ++ ENTER(); ++ ++ sdio_claim_host(card->func); ++ ret = disable_host_int_mask(priv, HIM_DISABLE); ++ sdio_release_host(card->func); ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function enables the host interrupts. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS ++ */ ++int ++sbi_enable_host_int(uap_private * priv) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ int ret; ++ ++ ENTER(); ++ ++ sdio_claim_host(card->func); ++ ret = enable_host_int_mask(priv, HIM_ENABLE); ++ sdio_release_host(card->func); ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function de-registers the device. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS ++ */ ++int ++sbi_unregister_dev(uap_private * priv) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "Error: card or function is NULL!\n"); ++ goto done; ++ } ++ ++ 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); ++ ++ done: ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++ ++/** ++ * @brief This function registers the device. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_register_dev(uap_private * priv) ++{ ++ int ret = UAP_STATUS_FAILURE; ++ u8 reg; ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ struct sdio_func *func; ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "Error: card or function is NULL!\n"); ++ goto done; ++ } ++ ++ func = card->func; ++ ++ /* Initialize the private structure */ ++ priv->uap_dev.ioport = 0; ++ ++ sdio_claim_host(func); ++ ++ ret = sdio_enable_func(func); ++ if (ret) { ++ PRINTM(FATAL, "sdio_enable_func() failed: ret=%d\n", ret); ++ goto release_host; ++ } ++ ++ ret = sdio_claim_irq(func, sbi_interrupt); ++ if (ret) { ++ PRINTM(FATAL, "sdio_claim_irq failed: ret=%d\n", ret); ++ goto disable_func; ++ } ++ ++ /* Read the IO port */ ++ ret = sbi_read_ioreg(priv, IO_PORT_0_REG, ®); ++ if (ret) ++ goto release_irq; ++ else ++ priv->uap_dev.ioport |= reg; ++ ++ ret = sbi_read_ioreg(priv, IO_PORT_1_REG, ®); ++ if (ret) ++ goto release_irq; ++ else ++ priv->uap_dev.ioport |= (reg << 8); ++ ++ ret = sbi_read_ioreg(priv, IO_PORT_2_REG, ®); ++ if (ret) ++ goto release_irq; ++ else ++ priv->uap_dev.ioport |= (reg << 16); ++ ++ PRINTM(INFO, "SDIO FUNC #%d IO port: 0x%x\n", func->num, ++ priv->uap_dev.ioport); ++ ++ ret = sdio_set_block_size(card->func, SD_BLOCK_SIZE); ++ if (ret) { ++ PRINTM(ERROR, "%s: cannot set SDIO block size\n", __FUNCTION__); ++ ret = UAP_STATUS_FAILURE; ++ goto release_irq; ++ } ++ priv->hotplug_device = &func->dev; ++ ++ if (helper_name == NULL) { ++ helper_name = DEFAULT_HELPER_NAME; ++ } ++ if (fw_name == NULL) { ++ fw_name = DEFAULT_FW_NAME; ++ } ++ sdio_release_host(func); ++ ++ sdio_set_drvdata(func, card); ++ ++ ret = UAP_STATUS_SUCCESS; ++ goto done; ++ ++ release_irq: ++ sdio_release_irq(func); ++ disable_func: ++ sdio_disable_func(func); ++ release_host: ++ sdio_release_host(func); ++ ++ done: ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function sends data to the card. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param payload A pointer to the data/cmd buffer ++ * @param nb the length of data/cmd ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_host_to_card(uap_private * priv, u8 * payload, u16 nb) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ int ret = UAP_STATUS_SUCCESS; ++ int buf_block_len; ++ int blksz; ++ int i = 0; ++ u8 *buf = NULL; ++#ifdef PXA3XX_DMA_ALIGN ++ void *tmpbuf = NULL; ++ int tmpbufsz; ++#endif ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "card or function is NULL!\n"); ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ buf = payload; ++#ifdef PXA3XX_DMA_ALIGN ++ if ((u32) payload & (PXA3XX_DMA_ALIGNMENT - 1)) { ++ tmpbufsz = ALIGN_SZ(nb, PXA3XX_DMA_ALIGNMENT); ++ tmpbuf = kmalloc(tmpbufsz, GFP_KERNEL); ++ memset(tmpbuf, 0, tmpbufsz); ++ /* Ensure 8-byte aligned CMD buffer */ ++ buf = (u8 *) ALIGN_ADDR(tmpbuf, PXA3XX_DMA_ALIGNMENT); ++ memcpy(buf, payload, nb); ++ } ++#endif ++ /* Allocate buffer and copy payload */ ++ blksz = SD_BLOCK_SIZE; ++ buf_block_len = (nb + blksz - 1) / blksz; ++ sdio_claim_host(card->func); ++#define MAX_WRITE_IOMEM_RETRY 2 ++ priv->uap_dev.cmd_sent = TRUE; ++ priv->uap_dev.data_sent = TRUE; ++ do { ++ /* Transfer data to card */ ++ ret = sdio_writesb(card->func, priv->uap_dev.ioport, buf, ++ buf_block_len * blksz); ++ if (ret < 0) { ++ i++; ++ PRINTM(ERROR, "host_to_card, write iomem (%d) failed: %d\n", i, ++ ret); ++ ret = UAP_STATUS_FAILURE; ++ if (i > MAX_WRITE_IOMEM_RETRY) ++ goto exit; ++ } else { ++ HEXDUMP("SDIO Blk Wr", payload, nb); ++ } ++ } while (ret == UAP_STATUS_FAILURE); ++ exit: ++ sdio_release_host(card->func); ++#ifdef PXA3XX_DMA_ALIGN ++ if (tmpbuf) ++ kfree(tmpbuf); ++#endif ++ if (ret == UAP_STATUS_FAILURE) { ++ priv->uap_dev.cmd_sent = FALSE; ++ priv->uap_dev.data_sent = FALSE; ++ } ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function reads CIS information. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param cisinfo A pointer to CIS information output buffer ++ * @param cislen A pointer to length of CIS info output buffer ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++#if 0 ++static int ++sbi_get_cis_info(uap_private * priv, void *cisinfo, int *cislen) ++{ ++#define CIS_PTR (0x8000) ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ unsigned int i, cis_ptr = CIS_PTR; ++ int ret = UAP_STATUS_FAILURE; ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "sbi_get_cis_info(): card or function is NULL!\n"); ++ goto exit; ++ } ++#define MAX_SDIO_CIS_INFO_LEN (256) ++ if (!cisinfo || (*cislen < MAX_SDIO_CIS_INFO_LEN)) { ++ PRINTM(WARN, "ERROR! get_cis_info: insufficient buffer passed\n"); ++ goto exit; ++ } ++ ++ *cislen = MAX_SDIO_CIS_INFO_LEN; ++ ++ sdio_claim_host(card->func); ++ ++ PRINTM(INFO, "cis_ptr=%#x\n", cis_ptr); ++ ++ /* Read the Tuple Data */ ++ for (i = 0; i < *cislen; i++) { ++ ((unsigned char *) cisinfo)[i] = ++ sdio_readb(card->func, cis_ptr + i, &ret); ++ if (ret) { ++ PRINTM(WARN, "get_cis_info error: ret=%d\n", ret); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ PRINTM(INFO, "cisinfo[%d]=%#x\n", i, ((unsigned char *) cisinfo)[i]); ++ } ++ ++ done: ++ sdio_release_host(card->func); ++ exit: ++ LEAVE(); ++ return ret; ++} ++#endif ++/** ++ * @brief This function downloads helper image to the card. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_prog_helper(uap_private * priv) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ u8 *helper = NULL; ++ int helperlen; ++ int ret = UAP_STATUS_SUCCESS; ++ void *tmphlprbuf = NULL; ++ int tmphlprbufsz; ++ u8 *hlprbuf; ++ int hlprblknow; ++ u32 tx_len; ++#ifdef FW_DOWNLOAD_SPEED ++ u32 tv1, tv2; ++#endif ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "sbi_prog_helper(): card or function is NULL!\n"); ++ goto done; ++ } ++ ++ if (priv->fw_helper) { ++ helper = (u8 *) priv->fw_helper->data; ++ helperlen = priv->fw_helper->size; ++ } else { ++ PRINTM(MSG, "No helper image found! Terminating download.\n"); ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ ++ PRINTM(INFO, "Downloading helper image (%d bytes), block size %d bytes\n", ++ helperlen, SD_BLOCK_SIZE); ++ ++#ifdef FW_DOWNLOAD_SPEED ++ tv1 = get_utimeofday(); ++#endif ++ ++#ifdef PXA3XX_DMA_ALIGN ++ tmphlprbufsz = ALIGN_SZ(UAP_UPLD_SIZE, PXA3XX_DMA_ALIGNMENT); ++#else /* !PXA3XX_DMA_ALIGN */ ++ tmphlprbufsz = UAP_UPLD_SIZE; ++#endif /* !PXA3XX_DMA_ALIGN */ ++ tmphlprbuf = kmalloc(tmphlprbufsz, GFP_KERNEL); ++ if (!tmphlprbuf) { ++ PRINTM(ERROR, ++ "Unable to allocate buffer for helper. Terminating download\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ memset(tmphlprbuf, 0, tmphlprbufsz); ++#ifdef PXA3XX_DMA_ALIGN ++ hlprbuf = (u8 *) ALIGN_ADDR(tmphlprbuf, PXA3XX_DMA_ALIGNMENT); ++#else /* !PXA3XX_DMA_ALIGN */ ++ hlprbuf = (u8 *) tmphlprbuf; ++#endif /* !PXA3XX_DMA_ALIGN */ ++ ++ sdio_claim_host(card->func); ++ ++ /* Perform helper data transfer */ ++ tx_len = (FIRMWARE_TRANSFER_NBLOCK * SD_BLOCK_SIZE) - INTF_HEADER_LEN; ++ hlprblknow = 0; ++ do { ++ /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ ++ ret = mv_sdio_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); ++ if (ret < 0) { ++ PRINTM(FATAL, "Helper download poll status timeout @ %d\n", ++ hlprblknow); ++ goto done; ++ } ++ ++ /* More data? */ ++ if (hlprblknow >= helperlen) ++ break; ++ ++ /* Set blocksize to transfer - checking for last block */ ++ if (helperlen - hlprblknow < tx_len) ++ tx_len = helperlen - hlprblknow; ++ ++ /* Set length to the 4-byte header */ ++ *(u32 *) hlprbuf = uap_cpu_to_le32(tx_len); ++ ++ /* Copy payload to buffer */ ++ memcpy(&hlprbuf[INTF_HEADER_LEN], &helper[hlprblknow], tx_len); ++ ++ PRINTM(INFO, "."); ++ ++ /* Send data */ ++ ret = sdio_writesb(card->func, priv->uap_dev.ioport, ++ hlprbuf, FIRMWARE_TRANSFER_NBLOCK * SD_BLOCK_SIZE); ++ ++ if (ret < 0) { ++ PRINTM(FATAL, "IO error during helper download @ %d\n", hlprblknow); ++ goto done; ++ } ++ ++ hlprblknow += tx_len; ++ } while (TRUE); ++ ++#ifdef FW_DOWNLOAD_SPEED ++ tv2 = get_utimeofday(); ++ PRINTM(INFO, "helper: %ld.%03ld.%03ld ", tv1 / 1000000, ++ (tv1 % 1000000) / 1000, tv1 % 1000); ++ PRINTM(INFO, " -> %ld.%03ld.%03ld ", tv2 / 1000000, (tv2 % 1000000) / 1000, ++ tv2 % 1000); ++ tv2 -= tv1; ++ PRINTM(INFO, " == %ld.%03ld.%03ld\n", tv2 / 1000000, (tv2 % 1000000) / 1000, ++ tv2 % 1000); ++#endif ++ ++ /* Write last EOF data */ ++ PRINTM(INFO, "\nTransferring helper image EOF block\n"); ++ memset(hlprbuf, 0x0, SD_BLOCK_SIZE); ++ ret = sdio_writesb(card->func, priv->uap_dev.ioport, ++ hlprbuf, SD_BLOCK_SIZE); ++ ++ if (ret < 0) { ++ PRINTM(FATAL, "IO error in writing helper image EOF block\n"); ++ goto done; ++ } ++ ++ ret = UAP_STATUS_SUCCESS; ++ ++ done: ++ sdio_release_host(card->func); ++ if (tmphlprbuf) ++ kfree(tmphlprbuf); ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function downloads firmware image to the card. ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_prog_fw_w_helper(uap_private * priv) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ u8 *firmware = NULL; ++ int firmwarelen; ++ u8 base0; ++ u8 base1; ++ int ret = UAP_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 ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "sbi_prog_fw_w_helper(): card or function is NULL!\n"); ++ goto done; ++ } ++ ++ if (priv->firmware) { ++ firmware = (u8 *) priv->firmware->data; ++ firmwarelen = priv->firmware->size; ++ } else { ++ PRINTM(MSG, "No firmware image found! Terminating download.\n"); ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ ++ PRINTM(INFO, "Downloading FW image (%d bytes)\n", firmwarelen); ++ ++#ifdef FW_DOWNLOAD_SPEED ++ tv1 = get_utimeofday(); ++#endif ++ ++#ifdef PXA3XX_DMA_ALIGN ++ tmpfwbufsz = ALIGN_SZ(UAP_UPLD_SIZE, PXA3XX_DMA_ALIGNMENT); ++#else /* PXA3XX_DMA_ALIGN */ ++ tmpfwbufsz = UAP_UPLD_SIZE; ++#endif /* PXA3XX_DMA_ALIGN */ ++ tmpfwbuf = kmalloc(tmpfwbufsz, GFP_KERNEL); ++ if (!tmpfwbuf) { ++ PRINTM(ERROR, ++ "Unable to allocate buffer for firmware. Terminating download.\n"); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ memset(tmpfwbuf, 0, tmpfwbufsz); ++#ifdef PXA3XX_DMA_ALIGN ++ /* Ensure 8-byte aligned firmware buffer */ ++ fwbuf = (u8 *) ALIGN_ADDR(tmpfwbuf, PXA3XX_DMA_ALIGNMENT); ++#else /* PXA3XX_DMA_ALIGN */ ++ fwbuf = (u8 *) tmpfwbuf; ++#endif /* PXA3XX_DMA_ALIGN */ ++ ++ sdio_claim_host(card->func); ++ ++ /* Perform firmware data transfer */ ++ offset = 0; ++ do { ++ /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ ++ ret = mv_sdio_poll_card_status(priv, CARD_IO_READY | DN_LD_CARD_RDY); ++ if (ret < 0) { ++ PRINTM(FATAL, "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++) { ++ if ((ret = sbi_read_ioreg(priv, HOST_F1_RD_BASE_0, &base0)) < 0) { ++ PRINTM(WARN, "Dev BASE0 register read failed:" ++ " base0=0x%04X(%d). Terminating download.\n", base0, ++ base0); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ if ((ret = sbi_read_ioreg(priv, HOST_F1_RD_BASE_1, &base1)) < 0) { ++ PRINTM(WARN, "Dev BASE1 register read failed:" ++ " base1=0x%04X(%d). Terminating download.\n", base1, ++ base1); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ len = (((u16) base1) << 8) | base0; ++ ++ /* For SD8688 wait until the length is not 0, 1 or 2 before ++ downloading the first FW block, since BOOT code writes the ++ register to indicate the helper/FW download winner, the value ++ could be 1 or 2 (Func1 or Func2). */ ++ if ((len && offset) || (len > 2)) ++ break; ++ udelay(10); ++ } ++ ++ if (len == 0) ++ break; ++ else if (len > UAP_UPLD_SIZE) { ++ PRINTM(FATAL, "FW download failure @ %d, invalid length %d\n", ++ offset, len); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ ++ txlen = len; ++ ++ if (len & BIT(0)) { ++ i++; ++ if (i > MAX_WRITE_IOMEM_RETRY) { ++ PRINTM(FATAL, ++ "FW download failure @ %d, over max retry count\n", ++ offset); ++ ret = UAP_STATUS_FAILURE; ++ goto done; ++ } ++ PRINTM(ERROR, "FW CRC error indicated by the helper:" ++ " len = 0x%04X, txlen = %d\n", len, txlen); ++ len &= ~BIT(0); ++ /* 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 - 1) / SD_BLOCK_SIZE; ++ ++ /* Copy payload to buffer */ ++ memcpy(fwbuf, &firmware[offset], txlen); ++ } ++ ++ /* Send data */ ++ ret = sdio_writesb(card->func, priv->uap_dev.ioport, ++ fwbuf, tx_blocks * SD_BLOCK_SIZE); ++ ++ if (ret < 0) { ++ PRINTM(ERROR, "FW download, write iomem (%d) failed @ %d\n", i, ++ offset); ++ if (sbi_write_ioreg(priv, CONFIGURATION_REG, 0x04) < 0) { ++ PRINTM(ERROR, "write ioreg failed (CFG)\n"); ++ } ++ } ++ ++ offset += txlen; ++ } while (TRUE); ++ ++ PRINTM(INFO, "\nFW download over, size %d bytes\n", offset); ++ ++ ret = UAP_STATUS_SUCCESS; ++ done: ++#ifdef FW_DOWNLOAD_SPEED ++ tv2 = get_utimeofday(); ++ PRINTM(INFO, "FW: %ld.%03ld.%03ld ", tv1 / 1000000, ++ (tv1 % 1000000) / 1000, tv1 % 1000); ++ PRINTM(INFO, " -> %ld.%03ld.%03ld ", tv2 / 1000000, ++ (tv2 % 1000000) / 1000, tv2 % 1000); ++ tv2 -= tv1; ++ PRINTM(INFO, " == %ld.%03ld.%03ld\n", tv2 / 1000000, ++ (tv2 % 1000000) / 1000, tv2 % 1000); ++#endif ++ sdio_release_host(card->func); ++ if (tmpfwbuf) ++ kfree(tmpfwbuf); ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function checks if the firmware is ready to accept ++ * command or not. ++ * ++ * @param priv A pointer to uap_private structure ++ * @param pollnum Poll number ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_check_fw_status(uap_private * priv, int pollnum) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ int ret = UAP_STATUS_SUCCESS; ++ u16 firmwarestat; ++ int tries; ++ ++ ENTER(); ++ ++ sdio_claim_host(card->func); ++ ++ /* Wait for firmware initialization event */ ++ for (tries = 0; tries < pollnum; tries++) { ++ if ((ret = sd_read_firmware_status(priv, &firmwarestat)) < 0) ++ continue; ++ if (firmwarestat == FIRMWARE_READY) { ++ ret = UAP_STATUS_SUCCESS; ++ break; ++ } else { ++ mdelay(10); ++ ret = UAP_STATUS_FAILURE; ++ } ++ } ++ ++ if (ret < 0) ++ goto done; ++ ++ ret = UAP_STATUS_SUCCESS; ++ sd_get_rx_unit(priv); ++ ++ done: ++ sdio_release_host(card->func); ++ ++ LEAVE(); ++ return ret; ++} ++ ++/** ++ * @brief This function set bus clock on/off ++ * ++ * @param priv A pointer to uap_private structure ++ * @param option TRUE--on , FALSE--off ++ * @return UAP_STATUS_SUCCESS ++ */ ++#if 0 ++static int ++sbi_set_bus_clock(uap_private * priv, u8 option) ++{ ++ ENTER(); ++ LEAVE(); ++ return UAP_STATUS_SUCCESS; ++} ++#endif ++ ++/** ++ * @brief This function wakeup firmware ++ * ++ * @param priv A pointer to uap_private structure ++ * @return UAP_STATUS_SUCCESS or UAP_STATUS_FAILURE ++ */ ++int ++sbi_wakeup_firmware(uap_private * priv) ++{ ++ struct sdio_mmc_card *card = priv->uap_dev.card; ++ int ret = UAP_STATUS_SUCCESS; ++ ++ ENTER(); ++ ++ if (!card || !card->func) { ++ PRINTM(ERROR, "card or function is NULL!\n"); ++ LEAVE(); ++ return UAP_STATUS_FAILURE; ++ } ++ sdio_claim_host(card->func); ++ sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret); ++ sdio_release_host(card->func); ++ LEAVE(); ++ return ret; ++} +diff --git a/drivers/net/wireless/libertas_uap/uap_sdio_mmc.h b/drivers/net/wireless/libertas_uap/uap_sdio_mmc.h +new file mode 100644 +index 0000000..191c67f +--- /dev/null ++++ b/drivers/net/wireless/libertas_uap/uap_sdio_mmc.h +@@ -0,0 +1,136 @@ ++/** @file uap_sdio_mmc.h ++ * @brief This file contains SDIO IF (interface) module ++ * related macros, enum, and structure. ++ * ++ * Copyright (C) 2007-2009, 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: ++ 10/10/07: initial version ++****************************************************/ ++ ++#ifndef _UAP_SDIO_MMC_H ++#define _UAP_SDIO_MMC_H ++ ++#include <linux/mmc/sdio.h> ++#include <linux/mmc/sdio_ids.h> ++#include <linux/mmc/sdio_func.h> ++#include <linux/mmc/card.h> ++ ++#include "uap_headers.h" ++ ++/** The number of times to try when waiting for downloaded firmware to ++ become active. (polling the scratch register). */ ++#define MAX_FIRMWARE_POLL_TRIES 100 ++ ++/** Firmware ready */ ++#define FIRMWARE_READY 0xfedc ++ ++/** Number of firmware blocks to transfer */ ++#define FIRMWARE_TRANSFER_NBLOCK 2 ++ ++/* Host Control Registers */ ++/** Host Control Registers : I/O port 0 */ ++#define IO_PORT_0_REG 0x00 ++/** Host Control Registers : I/O port 1 */ ++#define IO_PORT_1_REG 0x01 ++/** Host Control Registers : I/O port 2 */ ++#define IO_PORT_2_REG 0x02 ++ ++/** Host Control Registers : Configuration */ ++#define CONFIGURATION_REG 0x03 ++/** Host Control Registers : Host without Command 53 finish host */ ++#define HOST_WO_CMD53_FINISH_HOST (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 mask */ ++#define HOST_INT_MASK_REG 0x04 ++/** 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 0x05 ++/** 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 F1 read base 0 */ ++#define HOST_F1_RD_BASE_0 0x10 ++/** Host F1 read base 1 */ ++#define HOST_F1_RD_BASE_1 0x11 ++ ++/** Card Control Registers : Card status register */ ++#define CARD_STATUS_REG 0x20 ++/** 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 : Card OCR 0 register */ ++#define CARD_OCR_0_REG 0x34 ++/** Card Control Registers : Card OCR 1 register */ ++#define CARD_OCR_1_REG 0x35 ++ ++/** Firmware status 0 register */ ++#define CARD_FW_STATUS0_REG 0x40 ++/** Firmware status 1 register */ ++#define CARD_FW_STATUS1_REG 0x41 ++/** Rx length register */ ++#define CARD_RX_LEN_REG 0x42 ++/** Rx unit register */ ++#define CARD_RX_UNIT_REG 0x43 ++ ++/** Chip Id Register 0 */ ++#define CARD_CHIP_ID_0_REG 0x801c ++/** Chip Id Register 1 */ ++#define CARD_CHIP_ID_1_REG 0x801d ++ ++#ifdef PXA3XX_DMA_ALIGN ++/** DMA alignment value for PXA3XX platforms */ ++#define PXA3XX_DMA_ALIGNMENT 8 ++/** 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) \ ++ ((((u32)(p)) + (((u32)(a)) - 1)) & ~(((u32)(a)) - 1)) ++#endif /* PXA3XX_DMA_ALIGN */ ++ ++struct sdio_mmc_card ++{ ++ /** sdio_func structure pointer */ ++ struct sdio_func *func; ++ /** uap_private structure pointer */ ++ uap_private *priv; ++}; ++ ++#endif /* _UAP_SDIO_MMC_H */
hooks/post-receive -- IPFire 2.x development tree