Files
linux-legacy/drivers/net/wireless/ath6kl/os/linux/ar6000_android.c
Alan Tull 4f61210f20 ENGR00123047 move atheros fw to firmware imx repository
Firmware will be copied to the /lib/firmware/ath6k/AR6102 directory
so change the path in the driver.

Signed-off-by: Alan Tull <r80115@freescale.com>
2010-08-10 11:49:50 -05:00

622 lines
18 KiB
C

/*
*
* Copyright (c) 2004-2010 Atheros Communications Inc.
* All rights reserved.
*
*
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation;
//
// Software distributed under the License is distributed on an "AS
// IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
// implied. See the License for the specific language governing
// rights and limitations under the License.
//
//
*
*/
#include "ar6000_drv.h"
#include "htc.h"
#include <linux/vmalloc.h>
#include <linux/fs.h>
#ifdef CONFIG_PM
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
#include <linux/wakelock.h>
#endif
enum {
WLAN_PWR_CTRL_UP = 0,
WLAN_PWR_CTRL_CUT_PWR,
WLAN_PWR_CTRL_DEEP_SLEEP,
WLAN_PWR_CTRL_WOW
};
#include <linux/platform_device.h>
#include <linux/inetdevice.h>
#define IS_MAC_NULL(mac) (mac[0]==0 && mac[1]==0 && mac[2]==0 && mac[3]==0 && mac[4]==0 && mac[5]==0)
#define MAX_BUF (8*1024)
#ifdef DEBUG
#define ATH_DEBUG_DBG_LOG ATH_DEBUG_MAKE_MODULE_MASK(0)
static ATH_DEBUG_MASK_DESCRIPTION android_debug_desc[] = {
{ ATH_DEBUG_DBG_LOG , "Android Debug Logs"},
};
ATH_DEBUG_INSTANTIATE_MODULE_VAR(android,
"android",
"Android Driver Interface",
ATH_DEBUG_MASK_DEFAULTS | ATH_DEBUG_DBG_LOG,
ATH_DEBUG_DESCRIPTION_COUNT(android_debug_desc),
android_debug_desc);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
char fwpath[256] = "/lib/firmware/ath6k/AR6102";
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
int buspm = WLAN_PWR_CTRL_CUT_PWR;
int wow2mode = WLAN_PWR_CTRL_CUT_PWR;
#define HAVE_WLAN_PWR_IMPL 0
#if HAVE_WLAN_PWR_IMPL
/** @brief Disable SDIO clock source and mask all interrupt */
extern void plat_disable_wlan_slot(void);
/** @brief Enable SDIO clock source and unmask all interrupt */
extern void plat_enable_wlan_slot(void);
#else
#define plat_disable_wlan_slot()
#define plat_enable_wlan_slot()
#endif
#endif /* CONFIG_PM */
extern int bmienable;
extern int wlaninitmode;
extern unsigned int wmitimeout;
extern wait_queue_head_t arEvent;
extern struct net_device *ar6000_devices[];
#ifdef CONFIG_HOST_TCMD_SUPPORT
extern unsigned int testmode;
#endif
extern char ifname[];
const char def_ifname[] = "wlan0";
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
module_param_string(fwpath, fwpath, sizeof(fwpath), 0644);
module_param(buspm, int, 0644);
#else
#define __user
/* for linux 2.4 and lower */
MODULE_PARM(buspm,"i");
#endif
struct wake_lock ar6k_init_wake_lock;
struct wake_lock ar6k_wow_wake_lock;
static A_STATUS (*ar6000_avail_ev_p)(void *, void *);
extern int ar6000_init(struct net_device *dev);
extern A_STATUS ar6000_configure_target(AR_SOFTC_T *ar);
extern void ar6000_stop_endpoint(struct net_device *dev, A_BOOL keepprofile);
extern A_STATUS ar6000_sysfs_bmi_get_config(AR_SOFTC_T *ar, A_UINT32 mode);
extern void ar6000_destroy(struct net_device *dev, unsigned int unregister);
static void ar6000_enable_mmchost_detect_change(int enable);
static void ar6000_restart_endpoint(struct net_device *dev);
#if defined(CONFIG_PM)
static A_STATUS ar6000_suspend_ev(void *context);
static A_STATUS ar6000_resume_ev(void *context);
#endif
int android_request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device)
{
struct file *filp = (struct file *)-ENOENT;
int ret = 0;
mm_segment_t oldfs;
struct firmware *firmware;
char filename[2048];
#if 0
const char *raw_filename = strrchr(name, '/');
#else
const char *raw_filename = NULL;
#endif
*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
if (!firmware)
return -ENOMEM;
if (raw_filename)
++raw_filename;
else
raw_filename = name;
sprintf(filename, "%s/%s", fwpath, raw_filename);
// Open file
oldfs = get_fs();
set_fs(KERNEL_DS);
do {
size_t length, bufsize, bmisize;
struct inode *inode;
filp = filp_open(filename, O_RDONLY, S_IRUSR);
if (IS_ERR(filp) || !filp->f_op) {
printk("%s: file %s filp_open error\n", __FUNCTION__, filename);
ret = -1;
break;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
inode = filp->f_path.dentry->d_inode;
#else
inode = filp->f_dentry->d_inode;
#endif
if (!inode) {
printk("%s: Get inode from filp failed\n", __FUNCTION__);
ret = -1;
break;
}
length = i_size_read(inode->i_mapping->host);
bufsize = ALIGN(length, PAGE_SIZE);
bmisize = A_ROUND_UP(length, 4);
bufsize = max(bmisize, bufsize);
firmware->data = vmalloc(bufsize);
firmware->size = bmisize;
if (!firmware->data) {
printk("Cannot allocate buffer for firmware\n");
ret = -ENOMEM;
break;
}
if (filp->f_op->read(filp, (char*)firmware->data, length, &filp->f_pos) != length) {
printk("%s: file read error, remaining=%d\n", __FUNCTION__, length);
ret = -1;
break;
}
} while (0);
if (!IS_ERR(filp)) {
filp_close(filp, NULL);
}
set_fs(oldfs);
if (ret!=0) {
if (firmware) {
if (firmware->data)
vfree(firmware->data);
kfree(firmware);
}
*firmware_p = NULL;
}
return ret;
}
void android_release_firmware(const struct firmware *firmware)
{
if (firmware) {
if (firmware->data)
vfree(firmware->data);
kfree(firmware);
}
}
#if defined(CONFIG_PM)
static void ar6k_send_asleep_event_to_app(AR_SOFTC_T *ar, A_BOOL asleep)
{
char buf[128];
union iwreq_data wrqu;
snprintf(buf, sizeof(buf), "HOST_ASLEEP=%s", asleep ? "asleep" : "awake");
A_MEMZERO(&wrqu, sizeof(wrqu));
wrqu.data.length = strlen(buf);
wireless_send_event(ar->arNetDev, IWEVCUSTOM, &wrqu, buf);
}
static void ar6000_wow_resume(AR_SOFTC_T *ar)
{
if (ar->arWowState) {
WMI_SET_HOST_SLEEP_MODE_CMD hostSleepMode = {TRUE, FALSE};
ar->arWowState = 0;
wmi_set_host_sleep_mode_cmd(ar->arWmi, &hostSleepMode);
wmi_scanparams_cmd(ar->arWmi, 0,0,60,0,0,0,0,0,0,0);
//wmi_set_keepalive_cmd(ar->arWmi, 0);
#if 1 /* we don't do it if the power consumption is already good enough. */
if (wmi_listeninterval_cmd(ar->arWmi, ar->arListenInterval, 0) == A_OK) {
}
#endif
ar6k_send_asleep_event_to_app(ar, FALSE);
if (ar->arTxPending[ar->arControlEp]) {
long timeleft = wait_event_interruptible_timeout(arEvent,
ar->arTxPending[ar->arControlEp] == 0, wmitimeout * HZ);
if (!timeleft || signal_pending(current)) {
printk("Failed to Resume Wow!!!!!!!!!!!!!!!!!!!!\n");
} else {
printk("Resume WoW successfully\n");
}
}
} else {
printk("WoW does not invoked. skip resume");
}
}
static void ar6000_wow_suspend(AR_SOFTC_T *ar)
{
#define ANDROID_WOW_LIST_ID 1
if (ar->arNetworkType != AP_NETWORK) {
/* Setup WoW for unicast & Aarp request for our own IP
disable background scan. Set listen interval into 1000 TUs
Enable keepliave for 110 seconds
*/
struct in_ifaddr **ifap = NULL;
struct in_ifaddr *ifa = NULL;
struct in_device *in_dev;
A_UINT8 macMask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
A_STATUS status;
WMI_ADD_WOW_PATTERN_CMD addWowCmd = { .filter = { 0 } };
WMI_DEL_WOW_PATTERN_CMD delWowCmd;
WMI_SET_HOST_SLEEP_MODE_CMD hostSleepMode = {FALSE, TRUE};
WMI_SET_WOW_MODE_CMD wowMode = { .enable_wow = TRUE };
ar6000_TxDataCleanup(ar); /* IMPORTANT, otherwise there will be 11mA after listen interval as 1000*/
#if 1 /* we don't do it if the power consumption is already good enough. */
if (wmi_listeninterval_cmd(ar->arWmi, A_MAX_WOW_LISTEN_INTERVAL, 0) == A_OK) {
}
#endif
// wmi_set_keepalive_cmd(ar->arWmi, 110); /* keepalive otherwise, we will be disconnected*/
status = wmi_scanparams_cmd(ar->arWmi, 0,0,0xffff,0,0,0,0,0,0,0);
wmi_set_wow_mode_cmd(ar->arWmi, &wowMode);
/* clear up our WoW pattern first */
delWowCmd.filter_list_id = ANDROID_WOW_LIST_ID;
delWowCmd.filter_id = 0;
wmi_del_wow_pattern_cmd(ar->arWmi, &delWowCmd);
/* setup unicast packet pattern for WoW */
if (ar->arNetDev->dev_addr[1]) {
addWowCmd.filter_list_id = ANDROID_WOW_LIST_ID;
addWowCmd.filter_size = 6; /* MAC address */
addWowCmd.filter_offset = 2;
status = wmi_add_wow_pattern_cmd(ar->arWmi, &addWowCmd, ar->arNetDev->dev_addr, macMask, addWowCmd.filter_size);
}
/* setup ARP request for our own IP */
if ((in_dev = __in_dev_get_rtnl(ar->arNetDev)) != NULL) {
for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; ifap = &ifa->ifa_next) {
if (!strcmp(ar->arNetDev->name, ifa->ifa_label)) {
break; /* found */
}
}
}
if (ifa && ifa->ifa_local) {
WMI_SET_IP_CMD ipCmd;
memset(&ipCmd, 0, sizeof(ipCmd));
ipCmd.ips[0] = ifa->ifa_local;
status = wmi_set_ip_cmd(ar->arWmi, &ipCmd);
}
ar6k_send_asleep_event_to_app(ar, TRUE);
wmi_set_host_sleep_mode_cmd(ar->arWmi, &hostSleepMode);
if (ar->arTxPending[ar->arControlEp]) {
long timeleft = wait_event_interruptible_timeout(arEvent,
ar->arTxPending[ar->arControlEp] == 0, wmitimeout * HZ);
if (!timeleft || signal_pending(current)) {
/* what can I do? wow resume at once */
printk("Fail to setup WoW\n");
} else {
ar->arWowState = 1;
printk("Setup WoW successfully\n");
}
}
mdelay(10);
} else {
printk("Not allowed to go to WOW at this moment.\n");
}
}
static void ar6000_pwr_on(AR_SOFTC_T *ar)
{
if (ar == NULL) {
/* turn on for all cards */
}
printk("%s --enter\n", __func__);
}
static void ar6000_pwr_down(AR_SOFTC_T *ar)
{
if (ar == NULL) {
/* shutdown for all cards */
}
printk("%s --enter\n", __func__);
}
static A_STATUS ar6000_suspend_ev(void *context)
{
int pmmode = buspm;
AR_SOFTC_T *ar = (AR_SOFTC_T *)context;
printk("%s: enter ar %p devices %p\n", __func__, ar, ar6000_devices[0]? netdev_priv(ar6000_devices[0]) : NULL);
wow_not_connected:
switch (pmmode) {
case WLAN_PWR_CTRL_DEEP_SLEEP:
ar6000_set_wlan_state(ar, WLAN_DISABLED);
ar->arOsPowerCtrl = WLAN_PWR_CTRL_DEEP_SLEEP;
return A_EBUSY;
case WLAN_PWR_CTRL_WOW:
if (ar->arWmiReady && ar->arWlanState==WLAN_ENABLED && ar->arConnected) {
ar->arOsPowerCtrl = WLAN_PWR_CTRL_WOW;
/* leave for pm_device to setup wow */
return A_EBUSY;
} else {
pmmode = wow2mode;
goto wow_not_connected;
}
break;
case WLAN_PWR_CTRL_CUT_PWR:
/* fall through */
default:
ar->arOsPowerCtrl = WLAN_PWR_CTRL_CUT_PWR;
ar6000_stop_endpoint(ar->arNetDev, TRUE);
ar->arWlanState = WLAN_DISABLED;
break;
}
return A_OK;
}
static A_STATUS ar6000_resume_ev(void *context)
{
AR_SOFTC_T *ar = (AR_SOFTC_T *)context;
A_UINT16 powerCtrl = ar->arOsPowerCtrl;
wake_lock(&ar6k_init_wake_lock);
printk("%s: enter\n", __func__);
ar->arOsPowerCtrl = WLAN_PWR_CTRL_UP;
switch (powerCtrl) {
case WLAN_PWR_CTRL_WOW:
printk("Warning! resume but osPowerCtl is not clear\n");
break;
case WLAN_PWR_CTRL_CUT_PWR:
ar6000_restart_endpoint(ar->arNetDev);
break;
case WLAN_PWR_CTRL_DEEP_SLEEP:
ar6000_set_wlan_state(ar, WLAN_ENABLED);
break;
default:
printk("Strange SDIO bus power mode!!\n");
break;
}
wake_unlock(&ar6k_init_wake_lock);
return A_OK;
}
static A_STATUS ar6000_android_avail_ev(void *context, void *hif_handle)
{
A_STATUS ret;
wake_lock(&ar6k_init_wake_lock);
ret = ar6000_avail_ev_p(context, hif_handle);
wake_unlock(&ar6k_init_wake_lock);
return ret;
}
static int ar6000_pm_suspend(struct platform_device *dev, pm_message_t state)
{
int i;
for (i = 0; i < MAX_AR6000; i++) {
AR_SOFTC_T *ar;
if (ar6000_devices[i] == NULL)
continue;
ar = (AR_SOFTC_T*)netdev_priv(ar6000_devices[i]);
printk("%s: enter\n", __func__);
switch (ar->arOsPowerCtrl) {
case WLAN_PWR_CTRL_CUT_PWR:
ar6000_pwr_down(ar);
break;
case WLAN_PWR_CTRL_WOW:
ar6000_wow_suspend(ar);
plat_disable_wlan_slot();
break;
case WLAN_PWR_CTRL_DEEP_SLEEP:
/* nothing to do. keep the power on */
break;
default:
printk("Something is strange for ar6000_pm_suspend %d\n", ar->arOsPowerCtrl);
break;
}
}
return 0;
}
static int ar6000_pm_resume(struct platform_device *dev)
{
int i;
for (i = 0; i < MAX_AR6000; i++) {
AR_SOFTC_T *ar;
if (ar6000_devices[i] == NULL)
continue;
printk("%s: enter\n", __func__);
ar = (AR_SOFTC_T*)netdev_priv(ar6000_devices[i]);
switch (ar->arOsPowerCtrl) {
case WLAN_PWR_CTRL_CUT_PWR:
ar6000_pwr_on(ar);
break;
case WLAN_PWR_CTRL_WOW:
wake_lock_timeout(&ar6k_wow_wake_lock, 3*HZ);
plat_enable_wlan_slot();
ar6000_wow_resume(ar);
ar->arOsPowerCtrl = WLAN_PWR_CTRL_UP;
break;
case WLAN_PWR_CTRL_DEEP_SLEEP:
/* nothing to do. keep the power on */
break;
default:
printk("Something is strange for ar6000_pm_resume %d\n", ar->arOsPowerCtrl);
break;
}
}
return 0;
}
static int ar6000_pm_probe(struct platform_device *pdev)
{
ar6000_pwr_on(NULL);
return 0;
}
static int ar6000_pm_remove(struct platform_device *pdev)
{
ar6000_pwr_down(NULL);
return 0;
}
static struct platform_driver ar6000_pm_device = {
.probe = ar6000_pm_probe,
.remove = ar6000_pm_remove,
.suspend = ar6000_pm_suspend,
.resume = ar6000_pm_resume,
.driver = {
.name = "wlan_ar6000_pm_dev",
},
};
#endif /* CONFIG_PM */
/* Useful for qualcom platform to detect our wlan card for mmc stack */
static void ar6000_enable_mmchost_detect_change(int enable)
{
#ifdef CONFIG_MMC_MSM
mm_segment_t oldfs;
struct file *filp = (struct file*)-ENOENT;
int length;
oldfs = get_fs();
set_fs(KERNEL_DS);
do {
char buf[3];
filp = filp_open("/sys/devices/platform/msm_sdcc.2/detect_change", O_RDWR, S_IRUSR);
if (IS_ERR(filp) || !filp->f_op)
break;
length = snprintf(buf, sizeof(buf), "%d\n", enable ? 1 : 0);
if (filp->f_op->write(filp, buf, length, &filp->f_pos) != length) {
break;
}
} while (0);
if (!IS_ERR(filp)) {
filp_close(filp, NULL);
}
set_fs(oldfs);
#endif
}
static void
ar6000_restart_endpoint(struct net_device *dev)
{
A_STATUS status = A_OK;
AR_SOFTC_T *ar = (AR_SOFTC_T*)netdev_priv(dev);
if (down_interruptible(&ar->arSem)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s(): down_interruptible failed \n", __func__));
return ;
}
if (ar->bIsDestroyProgress) {
up(&ar->arSem);
return;
}
BMIInit();
do {
A_BOOL rtnl_lock_held_on_entry;
if ( (status=ar6000_configure_target(ar))!=A_OK)
break;
if ( (status=ar6000_sysfs_bmi_get_config(ar, wlaninitmode)) != A_OK)
{
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("ar6000_avail: ar6000_sysfs_bmi_get_config failed\n"));
break;
}
rtnl_lock_held_on_entry = rtnl_trylock();
status = (ar6000_init(dev)==0) ? A_OK : A_ERROR;
if (rtnl_lock_held_on_entry) {
rtnl_unlock();
}
if (status!=A_OK) {
break;
}
ar->arWlanState = WLAN_ENABLED;
if (ar->arSsidLen) {
ar6000_connect_to_ap(ar);
}
} while (0);
up(&ar->arSem);
if (status==A_OK) {
return;
}
ar6000_devices[ar->arDeviceIndex] = NULL;
ar6000_destroy(ar->arNetDev, 1);
}
void android_module_init(OSDRV_CALLBACKS *osdrvCallbacks)
{
ar6000_enable_mmchost_detect_change(1);
bmienable = 1;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
if (ifname[0] == '\0')
strcpy(ifname, def_ifname);
#endif
if (wow2mode!=WLAN_PWR_CTRL_CUT_PWR && wow2mode!=WLAN_PWR_CTRL_DEEP_SLEEP) {
wow2mode=WLAN_PWR_CTRL_CUT_PWR;
}
wake_lock_init(&ar6k_init_wake_lock, WAKE_LOCK_SUSPEND, "ar6k_init");
wake_lock_init(&ar6k_wow_wake_lock, WAKE_LOCK_SUSPEND, "ar6k_wow");
#if defined(CONFIG_PM)
osdrvCallbacks->deviceSuspendHandler = ar6000_suspend_ev;
osdrvCallbacks->deviceResumeHandler = ar6000_resume_ev;
#endif
ar6000_avail_ev_p = osdrvCallbacks->deviceInsertedHandler;
osdrvCallbacks->deviceInsertedHandler = ar6000_android_avail_ev;
#if defined(CONFIG_PM)
/* Register ar6000_pm_device into system.
* We should also add platform_device into the first item of array devices[] in
* file arch/xxx/mach-xxx/board-xxxx.c
* Otherwise, WoW may not work properly since we may trigger WoW GPIO before system suspend
*/
if (platform_driver_register(&ar6000_pm_device))
printk("ar6000: fail to register the power control driver.\n");
#endif
}
void android_module_exit(void)
{
wake_lock_destroy(&ar6k_wow_wake_lock);
wake_lock_destroy(&ar6k_init_wake_lock);
#ifdef CONFIG_PM
platform_driver_unregister(&ar6000_pm_device);
#endif
ar6000_enable_mmchost_detect_change(1);
}
A_BOOL android_ar6k_endpoint_is_stop(AR_SOFTC_T *ar)
{
#ifdef CONFIG_PM
return ar->arOsPowerCtrl == WLAN_PWR_CTRL_CUT_PWR;
#else
return FALSE;
#endif
}
void android_ar6k_check_wow_status(AR_SOFTC_T *ar)
{
#ifdef CONFIG_PM
if (ar->arWowState) {
ar6000_wow_resume(ar);
}
#endif /* CONFIG_PM */
}
A_STATUS android_ar6k_start(AR_SOFTC_T *ar)
{
return A_OK;
}