blob: 1b526e3a3b2459f55719879d1a8d11b70b974dfd [file] [log] [blame]
//------------------------------------------------------------------------------
// Copyright (c) 2004-2010 Atheros Communications Inc.
// All rights reserved.
//
//
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
//
//
// Author(s): ="Atheros"
//------------------------------------------------------------------------------
#include "ar6000_drv.h"
#include "htc.h"
#include <linux/vmalloc.h>
#include <linux/fs.h>
#ifdef CONFIG_HAS_WAKELOCK
#include <linux/wakelock.h>
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#ifdef CONFIG_MMC_MSM
A_BOOL enable_mmc_host_detect_change = 1;
#else
A_BOOL enable_mmc_host_detect_change = 0;
#endif
static void ar6000_enable_mmchost_detect_change(int enable);
#if defined(CONFIG_MMC_MSM) && defined(CONFIG_ARCH_MSM7X27) && defined(CONFIG_MSM_SOC_REV_A)
#include <linux/device.h>
#include <mach/vreg.h>
#include <linux/gpio.h>
#include <mach/rpc_pmapp.h>
#include <linux/err.h>
#define WLAN_GPIO_EXT_POR_N 134
#define A0_CLOCK
static const char *id = "WLAN";
enum {
WLAN_VREG_L17 = 0,
WLAN_VREG_L19
};
struct wlan_vreg_info {
const char *vreg_id;
unsigned int vreg_level;
unsigned int pmapp_id;
unsigned int is_vreg_pin_controlled;
struct vreg *vreg;
};
static struct wlan_vreg_info vreg_info[] = {
{"bt", 3300, 21, 1, NULL},
{"wlan4", 1800, 23, 1, NULL},
};
void msm7x27a_wifi_power(bool on);
#endif /* CONFIG_MMC_MSM */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#ifndef KERNEL_VERSION_ANDROID
char fwpath[256] = "/lib/firmware/atheros/target/AR6003_844";
#else
char fwpath[256] = "/system/wifi";
#endif /*KERNEL_VERSION_ANDROID*/
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */
int wowledon;
unsigned int enablelogcat;
extern int bmienable;
extern struct net_device *ar6000_devices[];
extern char ifname[IFNAMSIZ];
#ifdef CONFIG_HAS_WAKELOCK
extern struct wake_lock ar6k_wow_wake_lock;
struct wake_lock ar6k_init_wake_lock;
#endif
extern int num_device;
const char def_ifname[] = "wlan0";
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
module_param_string(fwpath, fwpath, sizeof(fwpath), 0644);
module_param(enablelogcat, uint, 0644);
module_param(wowledon, int, 0644);
#else
#define __user
/* for linux 2.4 and lower */
MODULE_PARAM(wowledon,"i");
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
static int screen_is_off;
static struct early_suspend ar6k_early_suspend;
#endif
static A_STATUS (*ar6000_avail_ev_p)(void *, void *);
#if defined(ANDROID_ENV) && defined(CONFIG_ANDROID_LOGGER) && (!defined(CONFIG_MMC_MSM) || LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32))
int logger_write(const enum logidx index,
const unsigned char prio,
const char __kernel * const tag,
const char __kernel * const fmt,
...)
{
int ret = 0;
va_list vargs;
struct file *filp = (struct file *)-ENOENT;
mm_segment_t oldfs;
struct iovec vec[3];
int tag_bytes = strlen(tag) + 1, msg_bytes;
char *msg;
va_start(vargs, fmt);
msg = kvasprintf(GFP_ATOMIC, fmt, vargs);
va_end(vargs);
if (!msg)
return -ENOMEM;
if (in_interrupt()) {
/* we have no choice since aio_write may be blocked */
printk(KERN_ALERT "%s", msg);
goto out_free_message;
}
msg_bytes = strlen(msg) + 1;
if (msg_bytes <= 1) /* empty message? */
goto out_free_message; /* don't bother, then */
if ((msg_bytes + tag_bytes + 1) > 2048) {
ret = -E2BIG;
goto out_free_message;
}
vec[0].iov_base = (unsigned char *) &prio;
vec[0].iov_len = 1;
vec[1].iov_base = (void *) tag;
vec[1].iov_len = strlen(tag) + 1;
vec[2].iov_base = (void *) msg;
vec[2].iov_len = strlen(msg) + 1;
oldfs = get_fs();
set_fs(KERNEL_DS);
do {
filp = filp_open("/dev/log/main", O_WRONLY, S_IRUSR);
if (IS_ERR(filp) || !filp->f_op) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: filp_open /dev/log/main error\n", __FUNCTION__));
ret = -ENOENT;
break;
}
if (filp->f_op->aio_write) {
int nr_segs = sizeof(vec) / sizeof(vec[0]);
int len = vec[0].iov_len + vec[1].iov_len + vec[2].iov_len;
struct kiocb kiocb;
init_sync_kiocb(&kiocb, filp);
kiocb.ki_pos = 0;
kiocb.ki_left = len;
kiocb.ki_nbytes = len;
ret = filp->f_op->aio_write(&kiocb, vec, nr_segs, kiocb.ki_pos);
}
} while (0);
if (!IS_ERR(filp)) {
filp_close(filp, NULL);
}
set_fs(oldfs);
out_free_message:
if (msg) {
kfree(msg);
}
return ret;
}
#endif
int android_logger_lv(void *module, int mask)
{
switch (mask) {
case ATH_DEBUG_ERR:
return 6;
case ATH_DEBUG_INFO:
return 4;
case ATH_DEBUG_WARN:
return 5;
case ATH_DEBUG_TRC:
return 3;
default:
#ifdef DEBUG
if (!module) {
return 3;
} else if (module == &GET_ATH_MODULE_DEBUG_VAR_NAME(driver)) {
return (mask <=ATH_DEBUG_MAKE_MODULE_MASK(3)) ? 3 : 2;
} else if (module == &GET_ATH_MODULE_DEBUG_VAR_NAME(htc)) {
return 2;
} else {
return 3;
}
#else
return 3; /* DEBUG */
#endif
}
}
int android_readwrite_file(const A_CHAR *filename, A_CHAR *rbuf, const A_CHAR *wbuf, size_t length)
{
int ret = 0;
struct file *filp = (struct file *)-ENOENT;
mm_segment_t oldfs;
oldfs = get_fs();
set_fs(KERNEL_DS);
do {
int mode = (wbuf) ? O_RDWR : O_RDONLY;
filp = filp_open(filename, mode, S_IRUSR);
if (IS_ERR(filp) || !filp->f_op) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: file %s filp_open error\n", __FUNCTION__, filename));
ret = -ENOENT;
break;
}
if (length==0) {
/* Read the length of the file only */
struct inode *inode;
inode = GET_INODE_FROM_FILEP(filp);
if (!inode) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Get inode from %s failed\n", __FUNCTION__, filename));
ret = -ENOENT;
break;
}
ret = i_size_read(inode->i_mapping->host);
break;
}
if (wbuf) {
if ( (ret=filp->f_op->write(filp, wbuf, length, &filp->f_pos)) < 0) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Write %u bytes to file %s error %d\n", __FUNCTION__,
length, filename, ret));
break;
}
} else {
if ( (ret=filp->f_op->read(filp, rbuf, length, &filp->f_pos)) < 0) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("%s: Read %u bytes from file %s error %d\n", __FUNCTION__,
length, filename, ret));
break;
}
}
} while (0);
if (!IS_ERR(filp)) {
filp_close(filp, NULL);
}
set_fs(oldfs);
return ret;
}
int android_request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device)
{
int ret = 0;
struct firmware *firmware;
char filename[256];
const char *raw_filename = name;
*firmware_p = firmware = A_MALLOC(sizeof(*firmware));
if (!firmware)
return -ENOMEM;
A_MEMZERO(firmware, sizeof(*firmware));
do {
size_t length, bufsize, bmisize;
if (snprintf(filename, sizeof(filename), "%s/%s", fwpath,
raw_filename) >= sizeof(filename)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("snprintf: %s/%s\n", fwpath, raw_filename));
ret = -1;
break;
}
if ( (ret=android_readwrite_file(filename, NULL, NULL, 0)) < 0) {
break;
} else {
length = ret;
}
if (strcmp(raw_filename, "softmac") == 0) {
bufsize = length = 17;
} else {
bufsize = ALIGN(length, PAGE_SIZE);
bmisize = A_ROUND_UP(length, 4);
bufsize = max(bmisize, bufsize);
}
firmware->data = vmalloc(bufsize);
firmware->size = length;
AR_DEBUG_PRINTF(ATH_DEBUG_TRC, ("AR6K: %s(): raw_filename=%s, bufsize=%d\n", __FUNCTION__, raw_filename, bufsize));
if (!firmware->data) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: Cannot allocate buffer for firmware\n", __FUNCTION__));
ret = -ENOMEM;
break;
}
if ( (ret=android_readwrite_file(filename, (char*)firmware->data, NULL, length)) != length) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("%s: file read error, ret %d request %d\n", __FUNCTION__, ret, length));
ret = -1;
break;
}
} while (0);
if (ret<0) {
if (firmware) {
if (firmware->data)
vfree(firmware->data);
A_FREE(firmware);
}
*firmware_p = NULL;
} else {
ret = 0;
}
return ret;
}
void android_release_firmware(const struct firmware *firmware)
{
if (firmware) {
if (firmware->data)
vfree(firmware->data);
kfree(firmware);
}
}
static A_STATUS ar6000_android_avail_ev(void *context, void *hif_handle)
{
A_STATUS ret;
#ifdef CONFIG_HAS_WAKELOCK
wake_lock(&ar6k_init_wake_lock);
#endif
ar6000_enable_mmchost_detect_change(0);
ret = ar6000_avail_ev_p(context, hif_handle);
#ifdef CONFIG_HAS_WAKELOCK
wake_unlock(&ar6k_init_wake_lock);
#endif
return ret;
}
static int android_do_ioctl_direct(struct net_device *dev, int cmd, struct ifreq *ifr, void *data)
{
int ret = -EIO;
int (*do_ioctl)(struct net_device *, struct ifreq *, int);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
do_ioctl = dev->do_ioctl;
#else
do_ioctl = dev->netdev_ops->ndo_do_ioctl;
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29) */
ifr->ifr_ifru.ifru_data = (__force void __user *)data;
if (do_ioctl) {
mm_segment_t oldfs = get_fs();
set_fs(KERNEL_DS);
ret = do_ioctl(dev, ifr, cmd);
set_fs(oldfs);
}
return ret;
}
static int ar6000_cscan(struct net_device *dev, struct iw_point *data, char *cmdbuf)
{
char *ptr = cmdbuf;
int iocmd = SIOCSIWSCAN - SIOCSIWCOMMIT;
const iw_handler setScan = dev->wireless_handlers->standard[iocmd];
A_INT32 home_dwell=0, pas_dwell=0, act_dwell=0;
A_UCHAR ssid[IW_ESSID_MAX_SIZE+1] = { 0 };
A_INT32 ssid_len = 0, ie_len;
A_UINT8 index = 1; /* reserve index 0 for wext */
A_INT32 ch = 0,len=data->length ;
A_CHAR nprobe, scantype;
struct iw_freq chList[IW_MAX_FREQUENCIES];
A_UCHAR *scanBuf = (A_UCHAR*)(ptr+ 12);
A_UCHAR *scanEnd = (A_UCHAR*)(ptr + len);
A_BOOL broadcastSsid = FALSE;
AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev);
AR_SOFTC_STA_T *arSta = &arPriv->arSta;
while ( scanBuf < scanEnd )
{
A_UCHAR *sp = scanBuf;
switch (*scanBuf)
{
case 'S': /* SSID section */
if (ssid_len > 0 && index < MAX_PROBED_SSID_INDEX) {
/* setup the last parsed ssid, reserve index 0 for wext */
if (wmi_probedSsid_cmd(arPriv->arWmi, index,
SPECIFIC_SSID_FLAG, ssid_len, ssid) == A_OK) {
++index;
if (arSta->scanSpecificSsid<index) {
arSta->scanSpecificSsid = index;
}
}
}
ie_len = ((scanBuf + 1) < scanEnd) ? ((A_INT32)*(scanBuf+1) + 1) : 0;
if ((scanBuf+ie_len) < scanEnd ) {
ssid_len = *(scanBuf+1);
if (ssid_len == 0) {
broadcastSsid = TRUE;
} else {
A_MEMCPY(ssid, scanBuf+2, ssid_len);
ssid[ssid_len] = '\0';
}
}
scanBuf += 1 + ie_len;
break;
case 'C': /* Channel section */
if (scanBuf+1 < scanEnd) {
int value = *(scanBuf+1);
if (value == 0) {
ch = 0; /* scan for all channels */
} else if (ch < IW_MAX_FREQUENCIES) {
if (value>1000) {
chList[ch].e = 1;
chList[ch].m = value * 100000;
} else {
chList[ch].e = 0;
chList[ch].m = value;
}
++ch;
}
}
scanBuf += 2;
break;
case 'P': /* Passive dwell section */
if (scanBuf+2 < scanEnd) {
pas_dwell = *(scanBuf+1) + (*(scanBuf+2) << 8);
}
scanBuf += 3;
break;
case 'H': /* Home dwell section */
if (scanBuf+2 < scanEnd) {
home_dwell = *(scanBuf+1) + (*(scanBuf+2) << 8);
}
scanBuf += 3;
break;
case 'N': /* Number of probe section */
if (scanBuf+1 < scanEnd) {
nprobe = *(scanBuf+1);
}
scanBuf += 2;
break;
case 'A': /* Active dwell section */
if (scanBuf+2 < scanEnd) {
act_dwell = *(scanBuf+1) + (*(scanBuf+2) << 8);
}
scanBuf += 3;
break;
case 'T': /* Scan active type section */
if (scanBuf+1 < scanEnd) {
scantype = *(scanBuf+1);
}
scanBuf += 2;
break;
default:
break;
} /* switch */
if (sp == scanBuf) {
return -1; /* parsing error */
}
}/* while */
if (ssid_len>0)
{
A_UINT8 idx; /* Clean up the last specific scan items */
for (idx=index; idx<arSta->scanSpecificSsid; ++idx) {
wmi_probedSsid_cmd(arPriv->arWmi, idx, DISABLE_SSID_FLAG, 0, NULL);
}
arSta->scanSpecificSsid = index;
/*
* There is no way to know when we need to send broadcast probe in current Android wpa_supplicant_6
* combo scan implemenation. Always force to sent it here uniti future Android version will set
* the broadcast flags for combo scan.
*/
#if 0
if (broadcastSsid)
#endif
{
/* setup the last index as broadcast SSID for combo scan */
++arSta->scanSpecificSsid;
wmi_probedSsid_cmd(arPriv->arWmi, index, ANY_SSID_FLAG, 0, NULL);
}
}
if (pas_dwell>0) {
/* TODO: Should we change our passive dwell? There may be some impact for bt-coex */
}
if (home_dwell>0) {
/* TODO: Should we adjust home_dwell? How to pass it to wext handler? */
}
if (setScan) {
union iwreq_data miwr;
struct iw_request_info minfo;
struct iw_scan_req scanreq, *pScanReq = NULL;
A_MEMZERO(&minfo, sizeof(minfo));
A_MEMZERO(&miwr, sizeof(miwr));
A_MEMZERO(&scanreq, sizeof(scanreq));
if (ssid_len > 0) {
pScanReq = &scanreq;
memcpy(scanreq.essid, ssid, ssid_len);
scanreq.essid_len = ssid_len;
miwr.data.flags |= IW_SCAN_THIS_ESSID;
}
if (ch > 0) {
pScanReq = &scanreq;
scanreq.num_channels = ch;
memcpy(scanreq.channel_list, chList, ch * sizeof(chList[0]));
miwr.data.flags |= IW_SCAN_THIS_FREQ;
}
if (pScanReq) {
miwr.data.pointer = (__force void __user *)&scanreq;
miwr.data.length = sizeof(scanreq);
}
minfo.cmd = SIOCSIWSCAN;
return setScan(dev, &minfo, &miwr, (char*)pScanReq);
}
return -1;
}
int android_ioctl_siwpriv(struct net_device *dev,
struct iw_request_info *__info,
struct iw_point *data, char *__extra)
{
char cmd[32]; /* assume that android command will not excess 32 */
char buf[32];
char *cmdbuf;
int len = sizeof(cmd)-1;
AR_SOFTC_DEV_T *arPriv = (AR_SOFTC_DEV_T *)ar6k_priv(dev);
AR_SOFTC_STA_T *arSta = &arPriv->arSta;
if (!data->pointer) {
return -EOPNOTSUPP;
}
if (data->length < len) {
len = data->length;
}
if (copy_from_user(cmd, data->pointer, len)) {
return -EIO;
}
cmd[len] = 0;
if (strcasecmp(cmd, "RSSI")==0 || strcasecmp(cmd, "RSSI-APPROX") == 0) {
int rssi = -200;
struct iw_statistics *iwStats;
struct iw_statistics* (*get_iwstats)(struct net_device *);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
get_iwstats = dev->get_wireless_stats;
#else
get_iwstats = dev->wireless_handlers->get_wireless_stats;
#endif
if (get_iwstats && arPriv->arConnected) {
iwStats = get_iwstats(dev);
if (iwStats) {
rssi = iwStats->qual.qual;
if (rssi == 255)
rssi = -200;
else
rssi += (161 - 256);
}
}
len = snprintf(buf, data->length, "SSID rssi %d\n", rssi) + 1;
return (copy_to_user(data->pointer, buf, len)==0) ? len : -1;
} else if (strcasecmp(cmd, "LINKSPEED")==0) {
/* We skip to use SIOCGIWRATE since Android always asked LINKSPEED just after RSSI*/
unsigned int speed_mbps;
if (arPriv->arConnected) {
speed_mbps = arPriv->arTargetStats.tx_unicast_rate / 1000;
} else {
speed_mbps = 1;
}
len = snprintf(buf, data->length, "LinkSpeed %u\n", speed_mbps) + 1;
return (copy_to_user(data->pointer, buf, len)==0) ? len : -1;
} else if (memcmp(cmd, "CSCAN S\x01\x00\x00S\x00", 12)==0) {
int ret=0;
if (!(cmdbuf = A_MALLOC(data->length)))
return -ENOMEM;
if (copy_from_user(cmdbuf, data->pointer, data->length)) {
A_FREE(cmdbuf);
return -EFAULT;
}
ret = ar6000_cscan(dev,data, cmdbuf);
A_FREE(cmdbuf);
return ret;
} else if (strcasecmp(cmd, "MACADDR")==0) {
/* reply comes back in the form "Macaddr = XX:XX:XX:XX:XX:XX" where XX */
A_UCHAR *mac = dev->dev_addr;
len = snprintf(buf, data->length, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2],
mac[3], mac[4], mac[5]) + 1;
return (copy_to_user(data->pointer, buf, len)==0) ? len : -1;
} else if (strcasecmp(cmd, "SCAN-ACTIVE")==0) {
return 0; /* unsupport function. Suppress the error */
} else if (strcasecmp(cmd, "SCAN-PASSIVE")==0) {
return 0; /* unsupport function. Suppress the error */
} else if (strcasecmp(cmd, "START")==0 || strcasecmp(cmd, "STOP")==0) {
struct ifreq ifr;
char userBuf[16];
int ex_arg = (strcasecmp(cmd, "START")==0) ? WLAN_ENABLED : WLAN_DISABLED;
int ret;
A_MEMZERO(userBuf, sizeof(userBuf));
((int *)userBuf)[0] = AR6000_XIOCTRL_WMI_SET_WLAN_STATE;
((int *)userBuf)[1] = ex_arg;
ret = android_do_ioctl_direct(dev, AR6000_IOCTL_EXTENDED, &ifr, userBuf);
if (ret==0) {
/* Send wireless event which need by android supplicant */
union iwreq_data wrqu;
A_MEMZERO(&wrqu, sizeof(wrqu));
wrqu.data.length = strlen(cmd);
wireless_send_event(dev, IWEVCUSTOM, &wrqu, cmd);
}
return ret;
} else if (strncasecmp(cmd, "POWERMODE ", 10)==0) {
int mode;
if (sscanf(cmd, "%*s %d", &mode) == 1) {
int iocmd = SIOCSIWPOWER - SIOCSIWCOMMIT;
iw_handler setPower = dev->wireless_handlers->standard[iocmd];
if (setPower) {
union iwreq_data miwr;
struct iw_request_info minfo;
A_MEMZERO(&minfo, sizeof(minfo));
A_MEMZERO(&miwr, sizeof(miwr));
minfo.cmd = SIOCSIWPOWER;
if (mode == 0 /* auto */)
miwr.power.disabled = 0;
else if (mode == 1 /* active */)
miwr.power.disabled = 1;
else
return -1;
return setPower(dev, &minfo, &miwr, NULL);
}
}
return -1;
} else if (strcasecmp(cmd, "GETPOWER")==0) {
struct ifreq ifr;
int userBuf[2];
A_MEMZERO(userBuf, sizeof(userBuf));
((int *)userBuf)[0] = AR6000_XIOCTRL_WMI_GET_POWER_MODE;
if (android_do_ioctl_direct(dev, AR6000_IOCTL_EXTENDED, &ifr, userBuf)>=0) {
WMI_POWER_MODE_CMD *getPowerMode = (WMI_POWER_MODE_CMD *)userBuf;
len = snprintf(buf, data->length, "powermode = %u\n",
(getPowerMode->powerMode==MAX_PERF_POWER) ? 1/*active*/ : 0/*auto*/) + 1;
return (copy_to_user(data->pointer, buf, len)==0) ? len : -1;
}
return -1;
} else if (strcasecmp(cmd, "GETBAND")==0) {
int band; /*0: auto, 1: 5GHz only, 2: 2.4GHz Only*/
switch (arPriv->arPhyCapability) {
case WMI_11A_CAPABILITY:
case WMI_11NA_CAPABILITY:
band = 1;
break;
case WMI_11NG_CAPABILITY:
case WMI_11G_CAPABILITY:
band = 2;
break;
case WMI_11AG_CAPABILITY:
case WMI_11NAG_CAPABILITY:
default:
band = 0;
break;
}
len = snprintf(buf, data->length, "Band %d\n", band) + 1;
return (copy_to_user(data->pointer, buf, len)==0) ? len : -1;
} else if (strncasecmp(cmd, "SETBAND ", 8)==0) {
int band;
if (sscanf(cmd, "%*s %d", &band) == 1) {
switch (band) {
case 1: /* 5GHz Only*/
/* TODO: Using WMI_CHANNEL_PARAMS_CMD to disable all 5GHz channels? */
break;
case 2: /* 5GHz Only*/
/* TODO: Using WMI_CHANNEL_PARAMS_CMD to disable all 2.4GHz channels? */
break;
case 0: /* auto */
default:
break;
}
return 0;
}
return -1;
} else if (strncasecmp(cmd, "SETSUSPENDOPT ", 14)==0) {
int enable;
if (sscanf(cmd, "%*s %d", &enable)==1) {
/*
* We set our suspend mode by wlan_config.h now.
* Should we follow Android command?? TODO
*/
return 0;
}
return -1;
} else if (strcasecmp(cmd, "SCAN-CHANNELS")==0) {
// reply comes back in the form "Scan-Channels = X" where X is the number of channels
int iocmd = SIOCGIWRANGE - SIOCSIWCOMMIT;
iw_handler getRange = dev->wireless_handlers->standard[iocmd];
if (getRange) {
union iwreq_data miwr;
struct iw_request_info minfo;
struct iw_range range;
A_MEMZERO(&minfo, sizeof(minfo));
A_MEMZERO(&miwr, sizeof(miwr));
A_MEMZERO(&range, sizeof(range));
minfo.cmd = SIOCGIWRANGE;
miwr.data.pointer = (__force void __user *) &range;
miwr.data.length = sizeof(range);
getRange(dev, &minfo, &miwr, (char*)&range);
}
if (arSta->arNumChannels!=-1) {
len = snprintf(buf, data->length, "Scan-Channels = %d\n", arSta->arNumChannels) + 1;
return (copy_to_user(data->pointer, buf, len)==0) ? len : -1;
}
return -1;
} else if (strncasecmp(cmd, "SCAN-CHANNELS ", 14)==0 ||
strncasecmp(cmd, "COUNTRY ", 8)==0) {
/*
* Set the available channels with WMI_SET_CHANNELPARAMS cmd
* However, the channels will be limited by the eeprom regulator domain
* Try to use a regulator domain which will not limited the channels range.
*/
int i;
int chan = 0;
A_UINT16 *clist;
struct ifreq ifr;
char ioBuf[256];
WMI_CHANNEL_PARAMS_CMD *chParamCmd = (WMI_CHANNEL_PARAMS_CMD *)ioBuf;
if (strncasecmp(cmd, "COUNTRY ", 8)==0) {
char *country = cmd + 8;
if (strcasecmp(country, "US")==0) {
chan = 11;
} else if (strcasecmp(country, "JP")==0) {
chan = 14;
} else if (strcasecmp(country, "EU")==0) {
chan = 13;
}
} else if (sscanf(cmd, "%*s %d", &chan) != 1) {
return -1;
}
if ( (chan != 11) && (chan != 13) && (chan != 14)) {
return -1;
}
if (arPriv->arNextMode == AP_NETWORK) {
return -1;
}
A_MEMZERO(&ifr, sizeof(ifr));
A_MEMZERO(ioBuf, sizeof(ioBuf));
chParamCmd->phyMode = WMI_11G_MODE;
clist = chParamCmd->channelList;
chParamCmd->numChannels = chan;
chParamCmd->scanParam = 1;
for (i = 0; i < chan; i++) {
clist[i] = wlan_ieee2freq(i + 1);
}
return android_do_ioctl_direct(dev, AR6000_IOCTL_WMI_SET_CHANNELPARAMS, &ifr, ioBuf);
} else if (strncasecmp(cmd, "BTCOEXMODE ", 11)==0) {
int mode;
if (sscanf(cmd, "%*s %d", &mode)==1) {
/*
* Android disable BT-COEX when obtaining dhcp packet except there is headset is connected
* It enable the BT-COEX after dhcp process is finished
* We ignore since we have our way to do bt-coex during dhcp obtaining.
*/
switch (mode) {
case 1: /* Disable*/
break;
case 0: /* Enable */
/* fall through */
case 2: /* Sense*/
/* fall through */
default:
break;
}
return 0; /* ignore it */
}
return -1;
} else if (strcasecmp(cmd, "BTCOEXSCAN-START")==0) {
/* Android enable or disable Bluetooth coexistence scan mode. When this mode is on,
* some of the low-level scan parameters used by the driver are changed to
* reduce interference with A2DP streaming.
*/
return 0; /* ignore it since we have btfilter */
} else if (strcasecmp(cmd, "BTCOEXSCAN-STOP")==0) {
return 0; /* ignore it since we have btfilter */
} else if (strncasecmp(cmd, "RXFILTER-ADD ", 13)==0) {
return 0; /* ignore it */
} else if (strncasecmp(cmd, "RXFILTER-REMOVE ", 16)==0) {
return 0; /* ignoret it */
} else if (strcasecmp(cmd, "RXFILTER-START")==0 || strcasecmp(cmd, "RXFILTER-STOP")==0) {
unsigned int flags = dev->flags;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
int mc_count = dev->mc_count;
#else
int mc_count = netdev_mc_count(dev);
#endif
if (strcasecmp(cmd, "RXFILTER-START")==0) {
if (mc_count > 0 || (flags & IFF_MULTICAST) ) {
flags &= ~IFF_MULTICAST;
}
} else {
flags |= IFF_MULTICAST;
}
if (flags != dev->flags) {
dev_change_flags(dev, flags);
}
return 0;
}
return -EOPNOTSUPP;
}
#if defined(CONFIG_MMC_MSM) && defined(CONFIG_ARCH_MSM7X27) && defined(CONFIG_MSM_SOC_REV_A)
void msm7x27a_wifi_power(bool on)
{
int rc = 0, index = 0;
static int resultFlag = 0, flag = 1;
for (index = 0; index < ARRAY_SIZE(vreg_info); index++) {
vreg_info[index].vreg = vreg_get(NULL,
vreg_info[index].vreg_id);
if (IS_ERR(vreg_info[index].vreg)) {
pr_err("%s:%s vreg get failed %ld\n",
__func__, vreg_info[index].vreg_id,
PTR_ERR(vreg_info[index].vreg));
rc = PTR_ERR(vreg_info[index].vreg);
if (on)
goto vreg_fail;
else
continue;
}
if (on) {
rc = vreg_set_level(vreg_info[index].vreg,
vreg_info[index].vreg_level);
if (rc) {
pr_err("%s:%s vreg set level failed %d\n",
__func__, vreg_info[index].vreg_id, rc);
goto vreg_fail;
}
rc = vreg_enable(vreg_info[index].vreg);
if (rc) {
pr_err("%s:%s vreg enable failed %d\n",
__func__,
vreg_info[index].vreg_id, rc);
goto vreg_fail;
}
if (vreg_info[index].is_vreg_pin_controlled) {
rc = pmapp_vreg_lpm_pincntrl_vote(id,
vreg_info[index].pmapp_id,
PMAPP_CLOCK_ID_A0, 1);
if (rc) {
pr_err("%s:%s pmapp_vreg_lpm_pincntrl_vote"
" for enable failed %d\n",
__func__,
vreg_info[index].vreg_id, rc);
goto vreg_fail;
}
}
if (index == WLAN_VREG_L17)
usleep(5);
else if (index == WLAN_VREG_L19)
usleep(10);
printk("\n vote for %s vreg. \n",vreg_info[index].vreg_id);
} else {
if (vreg_info[index].is_vreg_pin_controlled) {
rc = pmapp_vreg_lpm_pincntrl_vote(id,
vreg_info[index].pmapp_id,
PMAPP_CLOCK_ID_A0, 0);
if (rc) {
pr_err("%s:%s pmapp_vreg_lpm_pincntrl_vote"
" for disable failed %d\n",
__func__,
vreg_info[index].vreg_id, rc);
}
}
rc = vreg_disable(vreg_info[index].vreg);
if (rc) {
pr_err("%s:%s vreg disable failed %d\n",
__func__,
vreg_info[index].vreg_id, rc);
}
printk("\n vote against %s vreg. \n",vreg_info[index].vreg_id);
}
}
if (on) {
rc = gpio_request(WLAN_GPIO_EXT_POR_N, "WLAN_DEEP_SLEEP_N");
if (rc) {
pr_err("WLAN reset GPIO %d request failed %d\n",
WLAN_GPIO_EXT_POR_N, rc);
goto fail;
}
if(flag)
{
flag=0;
rc = gpio_direction_output(WLAN_GPIO_EXT_POR_N, 1);
if (rc < 0) {
pr_err("WLAN reset GPIO %d set direction failed %d\n",
WLAN_GPIO_EXT_POR_N, rc);
goto fail_gpio_dir_out;
}
}
#ifdef A0_CLOCK
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0, PMAPP_CLOCK_VOTE_ON);
printk("\nVote for A0 clock done\n");
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0, PMAPP_CLOCK_VOTE_PIN_CTRL);
if (rc) {
pr_err("%s: Configuring A0 clock to Pin controllable failed %d\n",
__func__, rc);
}
#else
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0, PMAPP_CLOCK_VOTE_OFF);
printk("\nVote against A0 clock done\n");
#endif
if (rc) {
pr_err("%s: Configuring A0 to turn off"
" failed %d\n", __func__, rc);
}
printk("\n vote for WLAN GPIO 134 done. \n");
} else {
if(!resultFlag){
gpio_set_value_cansleep(WLAN_GPIO_EXT_POR_N, 0);
rc = gpio_direction_input(WLAN_GPIO_EXT_POR_N);
if (rc) {
pr_err("WLAN reset GPIO %d set direction failed %d\n",
WLAN_GPIO_EXT_POR_N, rc);
}
gpio_free(WLAN_GPIO_EXT_POR_N);
printk("\n vote against WLAN GPIO 134 done. \n");
}
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
PMAPP_CLOCK_VOTE_OFF);
if (rc) {
pr_err("%s: Configuring A0 to turn OFF"
" failed %d\n", __func__, rc);
}
}
printk("Interface %s success \n",on?"initialization":"deinitialization");
resultFlag = 0;
return;
fail_gpio_dir_out:
gpio_free(WLAN_GPIO_EXT_POR_N);
vreg_fail:
index--;
while (index > 0) {
rc = vreg_disable(vreg_info[index].vreg);
if (rc) {
pr_err("%s:%s vreg disable failed %d\n",
__func__, vreg_info[index].vreg_id, rc);
}
index--;
}
if (!on)
goto fail;
fail:
resultFlag = 1;
printk("Interface %s failed \n",on?"initialization":"deinitialization");
return;
}
#endif
/* 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
#if defined(CONFIG_MMC_MSM) && defined(CONFIG_ARCH_MSM7X27) && defined(CONFIG_MSM_SOC_REV_A)
#define MMC_MSM_DEV "msm_sdcc.2"
#else
#define MMC_MSM_DEV "msm_sdcc.1"
#endif
char buf[3];
int length;
if (!enable_mmc_host_detect_change) {
return;
}
length = snprintf(buf, sizeof(buf), "%d\n", enable ? 1 : 0);
android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV "/polling", NULL, buf, length);
A_MDELAY(50);
#endif
}
#ifdef CONFIG_HAS_EARLYSUSPEND
static void android_early_suspend(struct early_suspend *h)
{
screen_is_off = 1;
}
static void android_late_resume(struct early_suspend *h)
{
screen_is_off = 0;
}
#endif
void android_module_init(OSDRV_CALLBACKS *osdrvCallbacks)
{
bmienable = 1;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
if (ifname[0] == '\0') {
if (strlcpy(ifname, def_ifname, sizeof(ifname)) >= sizeof(ifname)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("strlcpy: def_ifname %s\n", def_ifname));
return;
}
}
#endif
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_init(&ar6k_init_wake_lock, WAKE_LOCK_SUSPEND, "ar6k_init");
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
ar6k_early_suspend.suspend = android_early_suspend;
ar6k_early_suspend.resume = android_late_resume;
ar6k_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
register_early_suspend(&ar6k_early_suspend);
#endif
ar6000_avail_ev_p = osdrvCallbacks->deviceInsertedHandler;
osdrvCallbacks->deviceInsertedHandler = ar6000_android_avail_ev;
ar6000_enable_mmchost_detect_change(1);
}
void android_module_exit(void)
{
#ifdef CONFIG_HAS_EARLYSUSPEND
unregister_early_suspend(&ar6k_early_suspend);
#endif
#ifdef CONFIG_HAS_WAKELOCK
wake_lock_destroy(&ar6k_init_wake_lock);
#endif
ar6000_enable_mmchost_detect_change(1);
/* disable polling again after we remove our wlan card */
ar6000_enable_mmchost_detect_change(0);
}
#ifdef CONFIG_PM
void android_ar6k_check_wow_status(AR_SOFTC_T *ar, struct sk_buff *skb, A_BOOL isEvent)
{
#ifdef CONFIG_HAS_WAKELOCK
unsigned long wake_timeout = 5; /* 50 milli seconds for normal window's ping test */
#endif
AR_SOFTC_DEV_T *arPriv;
A_UINT8 i;
A_BOOL needWake = FALSE;
for(i = 0; i < num_device; i++)
{
arPriv = ar->arDev[i];
if (
#ifdef CONFIG_HAS_EARLYSUSPEND
screen_is_off &&
#endif
skb && arPriv->arConnected) {
if (isEvent) {
if (A_NETBUF_LEN(skb) >= sizeof(A_UINT16)) {
A_UINT16 cmd = *(const A_UINT16 *)A_NETBUF_DATA(skb);
switch (cmd) {
case WMI_CONNECT_EVENTID:
#ifdef CONFIG_HAS_WAKELOCK
wake_timeout = 5;
#endif
needWake = TRUE;
break;
default:
/* dont wake lock the system for other event */
break;
}
}
} else if (A_NETBUF_LEN(skb) >= sizeof(ATH_MAC_HDR)) {
ATH_MAC_HDR *datap = (ATH_MAC_HDR *)A_NETBUF_DATA(skb);
if (!IEEE80211_IS_MULTICAST(datap->dstMac)) {
switch (A_BE2CPU16(datap->typeOrLen)) {
case 0x0800: /* IP */
if (A_NETBUF_LEN(skb)>=24 &&
*((A_UCHAR*)A_NETBUF_DATA(skb)+23)==0x11) {
A_UCHAR *udpPkt = (A_UCHAR*)A_NETBUF_DATA(skb)+14;
A_UINT8 ihl = (*udpPkt & 0x0f) * sizeof(A_UINT32);
const A_UCHAR ipsec_keepalive[] = {
0x11, 0x94, 0x11, 0x94, 0x00, 0x09, 0x00, 0x00, 0xff
};
udpPkt += ihl;
if (A_NETBUF_LEN(skb)>=14+ihl+sizeof(ipsec_keepalive) &&
!memcmp(udpPkt, ipsec_keepalive, sizeof(ipsec_keepalive)-3) &&
udpPkt[8]==0xff) {
/*
* RFC 3948 UDP Encapsulation of IPsec ESP Packets
* Source and Destination port must be 4500
* Receivers MUST NOT depend upon the UDP checksum being zero
* Sender must use 1 byte payload with 0xff
* Receiver SHOULD ignore a received NAT-keepalive packet
*
* IPSec over UDP NAT keepalive packet. Just ignore
*/
break;
}
}
case 0x888e: /* EAPOL */
case 0x88c7: /* RSN_PREAUTH */
case 0x88b4: /* WAPI */
needWake = TRUE;
break;
case 0x0806: /* ARP is not important to hold wake lock */
needWake = (arPriv->arNetworkType==AP_NETWORK);
break;
default:
break;
}
} else if ( !IEEE80211_IS_BROADCAST(datap->dstMac) ) {
if (A_NETBUF_LEN(skb)>=14+20 ) {
/* check if it is mDNS packets */
A_UINT8 *dstIpAddr = (A_UINT8*)(A_NETBUF_DATA(skb)+14+20-4);
struct net_device *ndev = arPriv->arNetDev;
needWake = ((dstIpAddr[3] & 0xf8) == 0xf8) &&
(arPriv->arNetworkType==AP_NETWORK ||
(ndev->flags & IFF_ALLMULTI || ndev->flags & IFF_MULTICAST));
}
}else if (arPriv->arNetworkType==AP_NETWORK) {
switch (A_BE2CPU16(datap->typeOrLen)) {
case 0x0800: /* IP */
if (A_NETBUF_LEN(skb)>=14+20+2) {
A_UINT16 dstPort = *(A_UINT16*)(A_NETBUF_DATA(skb)+14+20);
dstPort = A_BE2CPU16(dstPort);
needWake = (dstPort == 0x43); /* dhcp request */
}
break;
case 0x0806:
needWake = TRUE;
default:
break;
}
}
}
}
}
if (needWake) {
#ifdef CONFIG_HAS_WAKELOCK
/* keep host wake up if there is any event and packate comming in*/
wake_lock_timeout(&ar6k_wow_wake_lock, wake_timeout);
#endif
if (wowledon) {
char buf[32];
int len = 0;
if ((len = snprintf(buf, sizeof(buf), "on")) >= sizeof(buf)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("snprintf: on\n"));
return;
}
android_readwrite_file("/sys/power/state", NULL, buf, len);
if ((len = snprintf(buf, sizeof(buf), "%d", 127)) >= sizeof(buf)) {
AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("snprintf: unable to write 127\n"));
return;
}
android_readwrite_file("/sys/class/leds/lcd-backlight/brightness",
NULL, buf,len);
}
}
}
#endif /* CONFIG_PM */
void android_send_reload_event(AR_SOFTC_DEV_T *arPriv)
{
struct net_device *ndev = arPriv->arNetDev;
union iwreq_data wrqu;
const char reloadEvt[] = "HANG";
A_MEMZERO(&wrqu, sizeof(wrqu));
wrqu.data.length = strlen(reloadEvt);
wireless_send_event(ndev, IWEVCUSTOM, &wrqu, reloadEvt);
}