diff options
Diffstat (limited to 'drivers/staging/ath6kl/os/linux/ar6000_android.c')
-rw-r--r-- | drivers/staging/ath6kl/os/linux/ar6000_android.c | 413 |
1 files changed, 413 insertions, 0 deletions
diff --git a/drivers/staging/ath6kl/os/linux/ar6000_android.c b/drivers/staging/ath6kl/os/linux/ar6000_android.c new file mode 100644 index 000000000000..a588825b9dab --- /dev/null +++ b/drivers/staging/ath6kl/os/linux/ar6000_android.c @@ -0,0 +1,413 @@ +//------------------------------------------------------------------------------ +// 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 + +A_BOOL enable_mmc_host_detect_change = 0; +static void ar6000_enable_mmchost_detect_change(int enable); + + +char fwpath[256] = "/system/wifi"; +int wowledon; +unsigned int enablelogcat; + +extern int bmienable; +extern struct net_device *ar6000_devices[]; +extern char ifname[]; + +#ifdef CONFIG_HAS_WAKELOCK +extern struct wake_lock ar6k_wow_wake_lock; +struct wake_lock ar6k_init_wake_lock; +#endif + +const char def_ifname[] = "wlan0"; +module_param_string(fwpath, fwpath, sizeof(fwpath), 0644); +module_param(enablelogcat, uint, 0644); +module_param(wowledon, int, 0644); + +#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(CONFIG_ANDROID_LOGGER) && (!defined(CONFIG_MMC_MSM)) +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 + } +} + +static 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 = kzalloc(sizeof(*firmware), GFP_KERNEL); + if (!firmware) + return -ENOMEM; + sprintf(filename, "%s/%s", fwpath, raw_filename); + do { + size_t length, bufsize, bmisize; + + if ( (ret=android_readwrite_file(filename, NULL, NULL, 0)) < 0) { + break; + } else { + length = ret; + } + + bufsize = ALIGN(length, PAGE_SIZE); + bmisize = A_ROUND_UP(length, 4); + bufsize = max(bmisize, bufsize); + firmware->data = vmalloc(bufsize); + firmware->size = length; + 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); + kfree(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; +} + +/* 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 +#define MMC_MSM_DEV "msm_sdcc.1" + char buf[3]; + int length; + + if (!enable_mmc_host_detect_change) { + return; + } + length = snprintf(buf, sizeof(buf), "%d\n", enable ? 1 : 0); + if (android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV "/detect_change", + NULL, buf, length) < 0) { + /* fall back to polling */ + android_readwrite_file("/sys/devices/platform/" MMC_MSM_DEV "/polling", NULL, buf, length); + } +#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 (ifname[0] == '\0') + strcpy(ifname, def_ifname); +#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); +} + +#ifdef CONFIG_PM +void android_ar6k_check_wow_status(AR_SOFTC_T *ar, struct sk_buff *skb, A_BOOL isEvent) +{ + if ( +#ifdef CONFIG_HAS_EARLYSUSPEND + screen_is_off && +#endif + skb && ar->arConnected) { + A_BOOL needWake = FALSE; + 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: + case WMI_DISCONNECT_EVENTID: + 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 */ + case 0x888e: /* EAPOL */ + case 0x88c7: /* RSN_PREAUTH */ + case 0x88b4: /* WAPI */ + needWake = TRUE; + break; + case 0x0806: /* ARP is not important to hold wake lock */ + default: + break; + } + } + } + if (needWake) { + /* keep host wake up if there is any event and packate comming in*/ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ar6k_wow_wake_lock, 3*HZ); +#endif + if (wowledon) { + char buf[32]; + int len = sprintf(buf, "on"); + android_readwrite_file("/sys/power/state", NULL, buf, len); + + len = sprintf(buf, "%d", 127); + android_readwrite_file("/sys/class/leds/lcd-backlight/brightness", + NULL, buf,len); + } + } + } +} +#endif /* CONFIG_PM */ |