From ae835810d5f429dd315bc8921575c97087c36d5b Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 13 Jul 2011 15:09:00 -0700 Subject: [PATCH] net: wireless: bcmdhd: Add PNO support through CFG80211 private command Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/Makefile | 2 +- drivers/net/wireless/bcmdhd/wl_android.c | 121 +++++++++++++++++++++++++++-- drivers/net/wireless/bcmdhd/wldev_common.h | 15 +++- 3 files changed, 129 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index 936d7ac..9f17fa7 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -7,7 +7,7 @@ DHDCFLAGS = -Wall -Wstrict-prototypes -Werror -Dlinux -DBCMDRIVER \ -DMMC_SDIO_ABORT -DBCMSDIO -DBCMLXSDMMC -DBCMPLATFORM_BUS -DWLP2P \ -DNEW_COMPAT_WIRELESS -DWIFI_ACT_FRAME -DARP_OFFLOAD_SUPPORT \ -DKEEP_ALIVE -DCSCAN -DGET_CUSTOM_MAC_ENABLE -DPKT_FILTER_SUPPORT \ - -DEMBEDDED_PLATFORM -DENABLE_INSMOD_NO_FW_LOAD \ + -DEMBEDDED_PLATFORM -DENABLE_INSMOD_NO_FW_LOAD -DPNO_SUPPORT \ -Idrivers/net/wireless/bcmdhd -Idrivers/net/wireless/bcmdhd/include DHDOFILES = aiutils.o bcmsdh_sdmmc_linux.o dhd_linux.o siutils.o bcmutils.o \ diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 6826a4c..432e253 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -70,6 +70,29 @@ #define CMD_GETBAND "GETBAND" #define CMD_COUNTRY "COUNTRY" +#ifdef PNO_SUPPORT +#define CMD_PNOSSIDCLR_SET "PNOSSIDCLR" +#define CMD_PNOSETUP_SET "PNOSETUP " +#define CMD_PNOENABLE_SET "PNOFORCE" +#define CMD_PNODEBUG_SET "PNODEBUG" + +#define PNO_TLV_PREFIX 'S' +#define PNO_TLV_VERSION '1' +#define PNO_TLV_SUBVERSION '2' +#define PNO_TLV_RESERVED '0' +#define PNO_TLV_TYPE_SSID_IE 'S' +#define PNO_TLV_TYPE_TIME 'T' +#define PNO_TLV_FREQ_REPEAT 'R' +#define PNO_TLV_FREQ_EXPO_MAX 'M' + +typedef struct cmd_tlv { + char prefix; + char version; + char subver; + char reserved; +} cmd_tlv_t; +#endif + typedef struct android_wifi_priv_cmd { char *buf; int used_len; @@ -82,9 +105,6 @@ typedef struct android_wifi_priv_cmd { void dhd_customer_gpio_wlan_ctrl(int onoff); uint dhd_dev_reset(struct net_device *dev, uint8 flag); void dhd_dev_init_ioctl(struct net_device *dev); -int net_os_set_dtim_skip(struct net_device *dev, int val); -int net_os_set_suspend_disable(struct net_device *dev, int val); -int net_os_set_suspend(struct net_device *dev, int val); extern bool ap_fw_loaded; #ifdef CUSTOMER_HW2 @@ -177,6 +197,85 @@ static int wl_android_get_band(struct net_device *dev, char *command, int total_ return bytes_written; } +#ifdef PNO_SUPPORT +static int wl_android_set_pno_setup(struct net_device *dev, char *command, int total_len) +{ + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + int res = -1; + int nssid = 0; + cmd_tlv_t *cmd_tlv_temp; + char *str_ptr; + int tlv_size_left; + int pno_time = 0; + int pno_repeat = 0; + int pno_freq_expo_max = 0; + + DHD_ERROR(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + + if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { + DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); + goto exit_proc; + } + + str_ptr = command + strlen(CMD_PNOSETUP_SET); + tlv_size_left = total_len - strlen(CMD_PNOSETUP_SET); + + cmd_tlv_temp = (cmd_tlv_t *)str_ptr; + memset(ssids_local, 0, sizeof(ssids_local)); + + if ((cmd_tlv_temp->prefix == PNO_TLV_PREFIX) && + (cmd_tlv_temp->version == PNO_TLV_VERSION) && + (cmd_tlv_temp->subver == PNO_TLV_SUBVERSION)) { + + str_ptr += sizeof(cmd_tlv_t); + tlv_size_left -= sizeof(cmd_tlv_t); + + if ((nssid = wl_iw_parse_ssid_list_tlv(&str_ptr, ssids_local, + MAX_PFN_LIST_COUNT, &tlv_size_left)) <= 0) { + DHD_ERROR(("SSID is not presented or corrupted ret=%d\n", nssid)); + goto exit_proc; + } else { + if ((str_ptr[0] != PNO_TLV_TYPE_TIME) || (tlv_size_left <= 1)) { + DHD_ERROR(("%s scan duration corrupted field size %d\n", + __FUNCTION__, tlv_size_left)); + goto exit_proc; + } + str_ptr++; + pno_time = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_time=%d\n", __FUNCTION__, pno_time)); + + if (str_ptr[0] != 0) { + if ((str_ptr[0] != PNO_TLV_FREQ_REPEAT)) { + DHD_ERROR(("%s pno repeat : corrupted field\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_repeat = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s :got pno_repeat=%d\n", __FUNCTION__, pno_repeat)); + if (str_ptr[0] != PNO_TLV_FREQ_EXPO_MAX) { + DHD_ERROR(("%s FREQ_EXPO_MAX corrupted field size\n", + __FUNCTION__)); + goto exit_proc; + } + str_ptr++; + pno_freq_expo_max = simple_strtoul(str_ptr, &str_ptr, 16); + DHD_INFO(("%s: pno_freq_expo_max=%d\n", + __FUNCTION__, pno_freq_expo_max)); + } + } + } else { + DHD_ERROR(("%s get wrong TLV command\n", __FUNCTION__)); + goto exit_proc; + } + + res = dhd_dev_pno_set(dev, ssids_local, nssid, pno_time, pno_repeat, pno_freq_expo_max); + +exit_proc: + return res; +} +#endif + /** * Global function definitions (declared in wl_android.h) */ @@ -252,7 +351,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) int bytes_written = 0; android_wifi_priv_cmd *priv_cmd; - /* net_os_wake_lock(dev); */ + net_os_wake_lock(net); priv_cmd = (android_wifi_priv_cmd*)ifr->ifr_data; if (!priv_cmd) @@ -340,6 +439,18 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { char *country_code = command + strlen(CMD_COUNTRY) + 1; bytes_written = wldev_set_country(net, country_code); +#ifdef PNO_SUPPORT + } + else if (strnicmp(command, CMD_PNOSSIDCLR_SET, strlen(CMD_PNOSSIDCLR_SET)) == 0) { + bytes_written = dhd_dev_pno_reset(net); + } + else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { + bytes_written = wl_android_set_pno_setup(net, command, priv_cmd->total_len); + } + else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { + uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; + bytes_written = dhd_dev_pno_enable(net, pfn_enabled); +#endif } else { DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command)); snprintf(command, 3, "OK"); @@ -358,7 +469,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) } exit: - /* net_os_wake_unlock(dev); */ + net_os_wake_unlock(net); if (command) { kfree(command); } diff --git a/drivers/net/wireless/bcmdhd/wldev_common.h b/drivers/net/wireless/bcmdhd/wldev_common.h index 28346cf..2c1d968 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.h +++ b/drivers/net/wireless/bcmdhd/wldev_common.h @@ -83,9 +83,18 @@ s32 wldev_iovar_getint_bsscfg( s32 wldev_iovar_setint_bsscfg( struct net_device *dev, s8 *iovar, s32 val, s32 bssidx); -void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); -void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); -int wldev_set_country(struct net_device *dev, char *country_code); +extern void get_customized_country_code(char *country_iso_code, wl_country_t *cspec); +extern void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec); +extern int wldev_set_country(struct net_device *dev, char *country_code); +extern int net_os_wake_lock(struct net_device *dev); +extern int net_os_wake_unlock(struct net_device *dev); +extern int net_os_wake_lock_timeout(struct net_device *dev); +extern int net_os_wake_lock_timeout_enable(struct net_device *dev); +extern int net_os_set_dtim_skip(struct net_device *dev, int val); +extern int net_os_set_suspend_disable(struct net_device *dev, int val); +extern int net_os_set_suspend(struct net_device *dev, int val); +extern int wl_iw_parse_ssid_list_tlv(char** list_str, wlc_ssid_t* ssid, + int max, int *bytes_left); /* Get the link speed from dongle, speed is in kpbs */ int wldev_get_link_speed(struct net_device *dev, int *plink_speed); -- 2.7.4