diff options
Diffstat (limited to 'drivers/net/wireless/libertas')
37 files changed, 19568 insertions, 0 deletions
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c new file mode 100644 index 00000000000..e0ecc4d483b --- /dev/null +++ b/drivers/net/wireless/libertas/11d.c @@ -0,0 +1,754 @@ +/** + * This file contains functions for 802.11D. + */ +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/wireless.h> + +#include "host.h" +#include "decl.h" +#include "11d.h" +#include "dev.h" +#include "wext.h" + +#define TX_PWR_DEFAULT 10 + +static struct region_code_mapping region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x10}, /* IC Canada */ + {"SG ", 0x10}, /* Singapore */ + {"EU ", 0x30}, /* ETSI */ + {"AU ", 0x30}, /* Australia */ + {"KR ", 0x30}, /* Republic Of Korea */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ +}; + +/* Following 2 structure defines the supported channels */ +static struct chan_freq_power channel_freq_power_UN_BG[] = { + {1, 2412, TX_PWR_DEFAULT}, + {2, 2417, TX_PWR_DEFAULT}, + {3, 2422, TX_PWR_DEFAULT}, + {4, 2427, TX_PWR_DEFAULT}, + {5, 2432, TX_PWR_DEFAULT}, + {6, 2437, TX_PWR_DEFAULT}, + {7, 2442, TX_PWR_DEFAULT}, + {8, 2447, TX_PWR_DEFAULT}, + {9, 2452, TX_PWR_DEFAULT}, + {10, 2457, TX_PWR_DEFAULT}, + {11, 2462, TX_PWR_DEFAULT}, + {12, 2467, TX_PWR_DEFAULT}, + {13, 2472, TX_PWR_DEFAULT}, + {14, 2484, TX_PWR_DEFAULT} +}; + +static u8 wlan_region_2_code(u8 * region) +{ + u8 i; + u8 size = sizeof(region_code_mapping)/ + sizeof(struct region_code_mapping); + + for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++) + region[i] = toupper(region[i]); + + for (i = 0; i < size; i++) { + if (!memcmp(region, region_code_mapping[i].region, + COUNTRY_CODE_LEN)) + return (region_code_mapping[i].code); + } + + /* default is US */ + return (region_code_mapping[0].code); +} + +static u8 *wlan_code_2_region(u8 code) +{ + u8 i; + u8 size = sizeof(region_code_mapping) + / sizeof(struct region_code_mapping); + for (i = 0; i < size; i++) { + if (region_code_mapping[i].code == code) + return (region_code_mapping[i].region); + } + /* default is US */ + return (region_code_mapping[0].region); +} + +/** + * @brief This function finds the nrchan-th chan after the firstchan + * @param band band + * @param firstchan first channel number + * @param nrchan number of channels + * @return the nrchan-th chan number +*/ +static u8 wlan_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 * chan) +/*find the nrchan-th chan after the firstchan*/ +{ + u8 i; + struct chan_freq_power *cfp; + u8 cfp_no; + + cfp = channel_freq_power_UN_BG; + cfp_no = sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cfp_no; i++) { + if ((cfp + i)->channel == firstchan) { + lbs_pr_debug(1, "firstchan found\n"); + break; + } + } + + if (i < cfp_no) { + /*if beyond the boundary */ + if (i + nrchan < cfp_no) { + *chan = (cfp + i + nrchan)->channel; + return 1; + } + } + + return 0; +} + +/** + * @brief This function Checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return TRUE; FALSE +*/ +static u8 wlan_channel_known_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr; + u8 nr_chan = parsed_region_chan->nr_chan; + u8 i = 0; + + lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr, + sizeof(struct chan_power_11d) * nr_chan); + + for (i = 0; i < nr_chan; i++) { + if (chan == chanpwr[i].chan) { + lbs_pr_debug(1, "11D: Found Chan:%d\n", chan); + return 1; + } + } + + lbs_pr_debug(1, "11D: Not Find Chan:%d\n", chan); + return 0; +} + +u32 libertas_chan_2_freq(u8 chan, u8 band) +{ + struct chan_freq_power *cf; + u16 cnt; + u16 i; + u32 freq = 0; + + cf = channel_freq_power_UN_BG; + cnt = + sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cnt; i++) { + if (chan == cf[i].channel) + freq = cf[i].freq; + } + + return freq; +} + +static int generate_domain_info_11d(struct parsed_region_chan_11d + *parsed_region_chan, + struct wlan_802_11d_domain_reg * domaininfo) +{ + u8 nr_subband = 0; + + u8 nr_chan = parsed_region_chan->nr_chan; + u8 nr_parsedchan = 0; + + u8 firstchan = 0, nextchan = 0, maxpwr = 0; + + u8 i, flag = 0; + + memcpy(domaininfo->countrycode, parsed_region_chan->countrycode, + COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D:nrchan=%d\n", nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan, + sizeof(struct parsed_region_chan_11d)); + + for (i = 0; i < nr_chan; i++) { + if (!flag) { + flag = 1; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + nr_parsedchan = 1; + continue; + } + + if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 && + parsed_region_chan->chanpwr[i].pwr == maxpwr) { + nextchan++; + nr_parsedchan++; + } else { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = + nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + } + } + + if (flag) { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + } + domaininfo->nr_subband = nr_subband; + + lbs_pr_debug(1, "nr_subband=%x\n", domaininfo->nr_subband); + lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo, + COUNTRY_CODE_LEN + 1 + + sizeof(struct ieeetypes_subbandset) * nr_subband); + return 0; +} + +/** + * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS + * @param region_chan pointer to struct region_channel + * @param *parsed_region_chan pointer to parsed_region_chan_11d + * @return N/A +*/ +static void wlan_generate_parsed_region_chan_11d(struct region_channel * region_chan, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 i; + struct chan_freq_power *cfp; + + if (region_chan == NULL) { + lbs_pr_debug(1, "11D: region_chan is NULL\n"); + return; + } + + cfp = region_chan->CFP; + if (cfp == NULL) { + lbs_pr_debug(1, "11D: cfp equal NULL \n"); + return; + } + + parsed_region_chan->band = region_chan->band; + parsed_region_chan->region = region_chan->region; + memcpy(parsed_region_chan->countrycode, + wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D: region[0x%x] band[%d]\n", parsed_region_chan->region, + parsed_region_chan->band); + + for (i = 0; i < region_chan->nrcfp; i++, cfp++) { + parsed_region_chan->chanpwr[i].chan = cfp->channel; + parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower; + lbs_pr_debug(1, "11D: Chan[%d] Pwr[%d]\n", + parsed_region_chan->chanpwr[i].chan, + parsed_region_chan->chanpwr[i].pwr); + } + parsed_region_chan->nr_chan = region_chan->nrcfp; + + lbs_pr_debug(1, "11D: nrchan[%d]\n", parsed_region_chan->nr_chan); + + return; +} + +/** + * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS + * @param region region ID + * @param band band + * @param chan chan + * @return TRUE;FALSE +*/ +static u8 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan) +{ + struct chan_freq_power *cfp; + int cfp_no; + u8 idx; + + ENTER(); + + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp == NULL) + return 0; + + for (idx = 0; idx < cfp_no; idx++) { + if (chan == (cfp + idx)->channel) { + /* If Mrvl Chip Supported? */ + if ((cfp + idx)->unsupported) { + return 0; + } else { + return 1; + } + } + } + + /*chan is not in the region table */ + LEAVE(); + return 0; +} + +/** + * @brief This function checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return 0 +*/ +static int parse_domain_info_11d(struct ieeetypes_countryinfofullset* + countryinfo, + u8 band, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 nr_subband, nrchan; + u8 lastchan, firstchan; + u8 region; + u8 curchan = 0; + + u8 idx = 0; /*chan index in parsed_region_chan */ + + u8 j, i; + + ENTER(); + + /*validation Rules: + 1. valid region Code + 2. First Chan increment + 3. channel range no overlap + 4. channel is valid? + 5. channel is supported by region? + 6. Others + */ + + lbs_dbg_hex("CountryInfo:", (u8 *) countryinfo, 30); + + if ((*(countryinfo->countrycode)) == 0 + || (countryinfo->len <= COUNTRY_CODE_LEN)) { + /* No region Info or Wrong region info: treat as No 11D info */ + LEAVE(); + return 0; + } + + /*Step1: check region_code */ + parsed_region_chan->region = region = + wlan_region_2_code(countryinfo->countrycode); + + lbs_pr_debug(1, "regioncode=%x\n", (u8) parsed_region_chan->region); + lbs_dbg_hex("CountryCode:", (char *)countryinfo->countrycode, + COUNTRY_CODE_LEN); + + parsed_region_chan->band = band; + + memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, + COUNTRY_CODE_LEN); + + nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) / + sizeof(struct ieeetypes_subbandset); + + for (j = 0, lastchan = 0; j < nr_subband; j++) { + + if (countryinfo->subband[j].firstchan <= lastchan) { + /*Step2&3. Check First Chan Num increment and no overlap */ + lbs_pr_debug(1, "11D: Chan[%d>%d] Overlap\n", + countryinfo->subband[j].firstchan, lastchan); + continue; + } + + firstchan = countryinfo->subband[j].firstchan; + nrchan = countryinfo->subband[j].nrchan; + + for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { + /*step4: channel is supported? */ + + if (!wlan_get_chan_11d(band, firstchan, i, &curchan)) { + /* Chan is not found in UN table */ + lbs_pr_debug(1, "chan is not supported: %d \n", i); + break; + } + + lastchan = curchan; + + if (wlan_region_chan_supported_11d + (region, band, curchan)) { + /*step5: Check if curchan is supported by mrvl in region */ + parsed_region_chan->chanpwr[idx].chan = curchan; + parsed_region_chan->chanpwr[idx].pwr = + countryinfo->subband[j].maxtxpwr; + idx++; + } else { + /*not supported and ignore the chan */ + lbs_pr_debug(1, + "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n", + i, curchan, region, band); + } + } + + /*Step6: Add other checking if any */ + + } + + parsed_region_chan->nr_chan = idx; + + lbs_pr_debug(1, "nrchan=%x\n", parsed_region_chan->nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan, + 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); + + LEAVE(); + return 0; +} + +/** + * @brief This function calculates the scan type for channels + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return PASSIVE if chan is unknown; ACTIVE if chan is known +*/ +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + u8 scan_type = cmd_scan_type_passive; + + ENTER(); + + if (wlan_channel_known_11d(chan, parsed_region_chan)) { + lbs_pr_debug(1, "11D: Found and do Active Scan\n"); + scan_type = cmd_scan_type_active; + } else { + lbs_pr_debug(1, "11D: Not Find and do Passive Scan\n"); + } + + LEAVE(); + return scan_type; + +} + +void libertas_init_11d(wlan_private * priv) +{ + priv->adapter->enable11d = 0; + memset(&(priv->adapter->parsed_region_chan), 0, + sizeof(struct parsed_region_chan_11d)); + return; +} + +static int wlan_enable_11d(wlan_private * priv, u8 flag) +{ + int ret; + + priv->adapter->enable11d = flag; + + /* send cmd to FW to enable/disable 11D function in FW */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11D_ENABLE, + &priv->adapter->enable11d); + if (ret) + lbs_pr_debug(1, "11D: Fail to enable 11D \n"); + + return 0; +} + +/** + * @brief This function sets DOMAIN INFO to FW + * @param priv pointer to wlan_private + * @return 0; -1 +*/ +static int set_domain_info_11d(wlan_private * priv) +{ + int ret; + + if (!priv->adapter->enable11d) { + lbs_pr_debug(1, "11D: dnld domain Info with 11d disabled\n"); + return 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + if (ret) + lbs_pr_debug(1, "11D: Fail to dnld domain Info\n"); + + return ret; +} + +/** + * @brief This function setups scan channels + * @param priv pointer to wlan_private + * @param band band + * @return 0 +*/ +int libertas_set_universaltable(wlan_private * priv, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + u16 size = sizeof(struct chan_freq_power); + u16 i = 0; + + memset(adapter->universal_channel, 0, + sizeof(adapter->universal_channel)); + + adapter->universal_channel[i].nrcfp = + sizeof(channel_freq_power_UN_BG) / size; + lbs_pr_debug(1, "11D: BG-band nrcfp=%d\n", + adapter->universal_channel[i].nrcfp); + + adapter->universal_channel[i].CFP = channel_freq_power_UN_BG; + adapter->universal_channel[i].valid = 1; + adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + adapter->universal_channel[i].band = band; + i++; + + return 0; +} + +/** + * @brief This function implements command CMD_802_11D_DOMAIN_INFO + * @param priv pointer to wlan_private + * @param cmd pointer to cmd buffer + * @param cmdno cmd ID + * @param cmdOption cmd action + * @return 0 +*/ +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdoption) +{ + struct cmd_ds_802_11d_domain_info *pdomaininfo = + &cmd->params.domaininfo; + struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain; + wlan_adapter *adapter = priv->adapter; + u8 nr_subband = adapter->domainreg.nr_subband; + + ENTER(); + + lbs_pr_debug(1, "nr_subband=%x\n", nr_subband); + + cmd->command = cpu_to_le16(cmdno); + pdomaininfo->action = cpu_to_le16(cmdoption); + if (cmdoption == cmd_act_get) { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd, + (int)(cmd->size)); + LEAVE(); + return 0; + } + + domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); + memcpy(domain->countrycode, adapter->domainreg.countrycode, + sizeof(domain->countrycode)); + + domain->header.len = + cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) + + sizeof(domain->countrycode)); + + if (nr_subband) { + memcpy(domain->subband, adapter->domainreg.subband, + nr_subband * sizeof(struct ieeetypes_subbandset)); + + cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + + domain->header.len + + sizeof(struct mrvlietypesheader) + + S_DS_GEN); + } else { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + } + + lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size)); + + LEAVE(); + + return 0; +} + +/** + * @brief This function implements private cmd: enable/disable 11D + * @param priv pointer to wlan_private + * @param wrq pointer to user data + * @return 0 or -1 + */ +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq) +{ + int data = 0; + int *val; + + ENTER(); + data = SUBCMD_DATA(wrq); + + lbs_pr_debug(1, "enable 11D: %s\n", + (data == 1) ? "enable" : "Disable"); + + wlan_enable_11d(priv, data); + val = (int *)wrq->u.name; + *val = priv->adapter->enable11d; + + LEAVE(); + return 0; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @param resp pointer to command response buffer + * @return 0; -1 + */ +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11d_domain_info + *domaininfo = &resp->params.domaininforesp; + struct mrvlietypes_domainparamset *domain = &domaininfo->domain; + u16 action = le16_to_cpu(domaininfo->action); + s16 ret = 0; + u8 nr_subband = 0; + + ENTER(); + + lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp, + (int)le16_to_cpu(resp->size)); + + nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset); + /* countrycode 3 bytes */ + + lbs_pr_debug(1, "11D Domain Info Resp: nr_subband=%d\n", nr_subband); + + if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { + lbs_pr_debug(1, "Invalid Numrer of Subband returned!!\n"); + return -1; + } + + switch (action) { + case cmd_act_set: /*Proc Set action */ + break; + + case cmd_act_get: + break; + default: + lbs_pr_debug(1, "Invalid action:%d\n", domaininfo->action); + ret = -1; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + if (priv->adapter->enable11d) { + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + ret = parse_domain_info_11d(&adapter->pattemptedbssdesc-> + countryinfo, 0, + &adapter->parsed_region_chan); + + if (ret == -1) { + lbs_pr_debug(1, "11D: Err Parse domain_info from AP..\n"); + LEAVE(); + return ret; + } + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + } + LEAVE(); + return 0; +} + +/** + * @brief This function generates 11D info from user specified regioncode and download to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_create_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + struct region_channel *region_chan; + u8 j; + + ENTER(); + lbs_pr_debug(1, "11D:curbssparams.band[%d]\n", adapter->curbssparams.band); + + if (priv->adapter->enable11d) { + /* update parsed_region_chan_11; dnld domaininf to FW */ + + for (j = 0; j < sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); j++) { + region_chan = &adapter->region_channel[j]; + + lbs_pr_debug(1, "11D:[%d] region_chan->band[%d]\n", j, + region_chan->band); + + if (!region_chan || !region_chan->valid + || !region_chan->CFP) + continue; + if (region_chan->band != adapter->curbssparams.band) + continue; + break; + } + + if (j >= sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0])) { + lbs_pr_debug(1, "11D:region_chan not found. band[%d]\n", + adapter->curbssparams.band); + LEAVE(); + return -1; + } + + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + wlan_generate_parsed_region_chan_11d(region_chan, + &adapter-> + parsed_region_chan); + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + + } + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h new file mode 100644 index 00000000000..db2ebea9f23 --- /dev/null +++ b/drivers/net/wireless/libertas/11d.h @@ -0,0 +1,105 @@ +/** + * This header file contains data structures and + * function declarations of 802.11d + */ +#ifndef _WLAN_11D_ +#define _WLAN_11D_ + +#include "types.h" +#include "defs.h" + +#define UNIVERSAL_REGION_CODE 0xff + +/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr) + */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +#define COUNTRY_CODE_LEN 3 +#define MAX_NO_OF_CHAN 40 + +struct cmd_ds_command; + +/** Data structure for Country IE*/ +struct ieeetypes_subbandset { + u8 firstchan; + u8 nrchan; + u8 maxtxpwr; +} __attribute__ ((packed)); + +struct ieeetypes_countryinfoset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +}; + +struct ieeetypes_countryinfofullset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +} __attribute__ ((packed)); + +struct mrvlietypes_domainparamset { + struct mrvlietypesheader header; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11d_domain_info { + u16 action; + struct mrvlietypes_domainparamset domain; +} __attribute__ ((packed)); + +/** domain regulatory information */ +struct wlan_802_11d_domain_reg { + /** country Code*/ + u8 countrycode[COUNTRY_CODE_LEN]; + /** No. of subband*/ + u8 nr_subband; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +}; + +struct chan_power_11d { + u8 chan; + u8 pwr; +} __attribute__ ((packed)); + +struct parsed_region_chan_11d { + u8 band; + u8 region; + s8 countrycode[COUNTRY_CODE_LEN]; + struct chan_power_11d chanpwr[MAX_NO_OF_CHAN]; + u8 nr_chan; +} __attribute__ ((packed)); + +struct region_code_mapping { + u8 region[COUNTRY_CODE_LEN]; + u8 code; +}; + +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d *parsed_region_chan); + +u32 libertas_chan_2_freq(u8 chan, u8 band); + +enum state_11d libertas_get_state_11d(wlan_private * priv); + +void libertas_init_11d(wlan_private * priv); + +int libertas_set_universaltable(wlan_private * priv, u8 band); + +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdOption); + +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq); + +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp); + +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv); + +int libertas_create_dnld_countryinfo_11d(wlan_private * priv); + +#endif /* _WLAN_11D_ */ diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE new file mode 100644 index 00000000000..8862742213b --- /dev/null +++ b/drivers/net/wireless/libertas/LICENSE @@ -0,0 +1,16 @@ + Copyright (c) 2003-2006, Marvell International Ltd. + All Rights Reserved + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile new file mode 100644 index 00000000000..56a8ea1fbf0 --- /dev/null +++ b/drivers/net/wireless/libertas/Makefile @@ -0,0 +1,12 @@ +usb8xxx-objs := main.o fw.o wext.o \ + rx.o tx.o cmd.o \ + cmdresp.o scan.o \ + join.o 11d.o \ + ioctl.o debugfs.o \ + ethtool.o assoc.o + +usb8xxx-objs += if_bootcmd.o +usb8xxx-objs += if_usb.o + +obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o + diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README new file mode 100644 index 00000000000..378577200b5 --- /dev/null +++ b/drivers/net/wireless/libertas/README @@ -0,0 +1,528 @@ +================================================================================ + README for USB8388 + + (c) Copyright © 2003-2006, Marvell International Ltd. + All Rights Reserved + + 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 license.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. +================================================================================ + +===================== +DRIVER LOADING +===================== + + o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ + + o. Load driver by using the following command: + + insmod usb8388.ko [fw_name=usb8388.bin] + +===================== +IWPRIV COMMAND +===================== + +NAME + This manual describes the usage of private commands used in Marvell WLAN + Linux Driver. All the commands available in Wlanconfig will not be available + in the iwpriv. + +SYNOPSIS + iwpriv <ethX> <command> [sub-command] ... + + iwpriv ethX setregioncode <n> + iwpriv ethX getregioncode + +Version 5 Command: + iwpriv ethX ledgpio <n> + +BT Commands: + The blinding table (BT) contains a list of mac addresses that should be + ignored by the firmware. It is primarily used for debugging and + testing networks. It can be edited and inspected with the following + commands: + + iwpriv ethX bt_reset + iwpriv ethX bt_add <mac_address> + iwpriv ethX bt_del <mac_address> + iwpriv ethX bt_list <id> + +FWT Commands: + The forwarding table (FWT) is a feature used to manage mesh network + routing in the firmware. The FWT is essentially a routing table that + associates a destination mac address (da) with a next hop receiver + address (ra). The FWT can be inspected and edited with the following + iwpriv commands, which are described in greater detail below. + Eventually, the table will be automatically maintained by a custom + routing protocol. + + NOTE: FWT commands replace the previous DFT commands. What were the DFT + commands?, you might ask. They were an earlier API to the firmware that + implemented a simple MAC-layer forwarding mechanism. In the unlikely + event that you were using these commands, you must migrate to the new + FWT commands which can be used to achieve the same functionality. + + iwpriv ethX fwt_add [parameters] + iwpriv ethX fwt_del [parameters] + iwpriv ethX fwt_lookup [parameters] + iwpriv ethX fwt_list [parameters] + iwpriv ethX fwt_list_route [parameters] + iwpriv ethX fwt_list_neigh [parameters] + iwpriv ethX fwt_reset [parameters] + iwpriv ethX fwt_cleanup + iwpriv ethX fwt_time + +MESH Commands: + + The MESH commands are used to configure various features of the mesh + routing protocol. The following commands are supported: + + iwpriv ethX mesh_get_ttl + iwpriv ethX mesh_set_ttl ttl + +DESCRIPTION + Those commands are used to send additional commands to the Marvell WLAN + card via the Linux device driver. + + The ethX parameter specifies the network device that is to be used to + perform this command on. it could be eth0, eth1 etc. + +setregioncode + This command is used to set the region code in the station. + where value is 'region code' for various regions like + USA FCC, Canada IC, Spain, France, Europe ETSI, Japan ... + + Usage: + iwpriv ethX setregioncode 0x10: set region code to USA (0x10). + +getregioncode + This command is used to get the region code information set in the + station. + +ledgpio + This command is used to set/get LEDs. + + iwpriv ethX ledgpio <LEDs> + will set the corresponding LED for the GPIO Line. + + iwpriv ethX ledgpio + will give u which LEDs are Enabled. + + Usage: + iwpriv eth1 ledgpio 1 0 2 1 3 4 + will enable + LED 1 -> GPIO 0 + LED 2 -> GPIO 1 + LED 3 -> GPIO 4 + + iwpriv eth1 ledgpio + shows LED information in the format as mentioned above. + + Note: LED0 is invalid + Note: Maximum Number of LEDs are 16. + +fwt_add + This command is used to insert an entry into the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_add da ra [metric dir ssn dsn hopcount ttl expiration sleepmode snr] + + The parameters between brackets are optional, but they must appear in + the order specified. For example, if you want to specify the metric, + you must also specify the dir, ssn, and dsn but you need not specify the + hopcount, expiration, sleepmode, or snr. Any unspecified parameters + will be assigned the defaults specified below. + + The different parameters are:- + da -- DA MAC address in the form 00:11:22:33:44:55 + ra -- RA MAC address in the form 00:11:22:33:44:55 + metric -- route metric (cost: smaller-metric routes are + preferred, default is 0) + dir -- direction (1 for direct, 0 for reverse, + default is 1) + ssn -- Source Sequence Number (time at the RA for + reverse routes. Default is 0) + dsn -- Destination Sequence Number (time at the DA + for direct routes. Default is 0) + hopcount -- hop count (currently unused, default is 0) + ttl -- TTL (Only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is + 1024us, or ~ 1ms. Use 0 for an indefinite + entry, default is 0) + sleepmode -- RA's sleep mode (currently unused, default is + 0) + snr -- SNR in the link to RA (currently unused, + default is 0) + + The command does not return anything. + +fwt_del + This command is used to remove an entry to the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_del da ra [dir] + + where the different parameters are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + dir -- direction (1 for direct, 0 for reverse, + default is 1) + + The command does not return anything. + +fwt_lookup + This command is used to get the best route in the FWT table to a given + host. The only parameter is the MAC address of the host that is being + looked for. + + iwpriv ethX fwt_lookup da + + where:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + + The command returns an output string identical to the one returned by + fwt_list described below. + + +fwt_list + This command is used to list a route from the FWT table. The only + parameter is the index into the table. If you want to list all the + routes in a table, start with index=0, and keep listing until you get a + "(null)" string. Note that the indicies may change as the fwt is + updated. It is expected that most users will not use fwt_list directly, + but that a utility similar to the traditional route command will be used + to invoke fwt_list over and over. + + iwpriv ethX fwt_list index + + The output is a string of the following form: + + da ra metric dir ssn dsn hopcount ttl expiration sleepmode snr + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + +fwt_list_route + This command is used to list a route from the FWT table. The only + parameter is the route ID. If you want to list all the routes in a + table, start with rid=0, and keep incrementing rid until you get a + "(null)" string. This function is similar to fwt_list. The only + difference is the output format. Also note that this command is meant + for debugging. It is expected that users will use fwt_lookup and + fwt_list. One important reason for this is that the route id may change + as the route table is altered. + + iwpriv ethX fwt_list_route rid + + The output is a string of the following form: + + da metric dir nid ssn dsn hopcount ttl expiration + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + nid -- Next-hop (neighbor) host ID (nid) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL count (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + +fwt_list_neigh + This command is used to list a neighbor from the FWT table. The only + parameter is the neighbor ID. If you want to list all the neighbors in a + table, start with nid=0, and keep incrementing nid until you get a + "(null)" string. Note that the nid from a fwt_list_route command can be + used as an input to this command. Also note that this command is meant + mostly for debugging. It is expected that users will use fwt_lookup. + One important reason for this is that the neighbor id may change as the + neighbor table is altered. + + iwpriv ethX fwt_list_neigh nid + + The output is a string of the following form: + + ra sleepmode snr references + + where the different fields are:- + ra -- RA MAC address (in the form "00:11:22:33:44:55") + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + references -- RA's reference counter + +fwt_reset + This command is used to reset the FWT table, getting rid of all the + entries. There are no input parameters. + + iwpriv ethX fwt_reset + + The command does not return anything. + +fwt_cleanup + This command is used to perform user-based garbage recollection. The + FWT table is checked, and all the entries that are expired or invalid + are cleaned. Note that this is exported to the driver for debugging + purposes, as garbage collection is also fired by the firmware when in + space problems. There are no input parameters. + + iwpriv ethX fwt_cleanup + + The command does returns the number of invalid/expired routes deleted. + +fwt_time + This command returns a card's internal time representation. It is this + time that is used to represent the expiration times of FWT entries. The + number is not consistent from card to card; it is simply a timer count. + The fwt_time command is used to inspect the timer so that expiration + times reported by fwt_list can be properly interpreted. + + iwpriv ethX fwt_time + +mesh_get_ttl + + The mesh ttl is the number of hops a mesh packet can traverse before it + is dropped. This parameter is used to prevent infinite loops in the + mesh network. The value returned by this function is the ttl assigned + to all mesh packets. Currently there is no way to control the ttl on a + per packet or per socket basis. + + iwpriv ethX mesh_get_ttl + +mesh_set_ttl ttl + + Set the ttl. The argument must be between 0 and 255. + + iwpriv ethX mesh_set_ttl <ttl> + +========================= +ETHTOOL +========================= + + +Use the -i option to retrieve version information from the driver. + +# ethtool -i eth0 +driver: libertas +version: COMM-USB8388-318.p4 +firmware-version: 5.110.7 +bus-info: + +Use the -e option to read the EEPROM contents of the card. + + Usage: + ethtool -e ethX [raw on|off] [offset N] [length N] + + -e retrieves and prints an EEPROM dump for the specified ethernet + device. When raw is enabled, then it dumps the raw EEPROM data + to stdout. The length and offset parameters allow dumping cer- + tain portions of the EEPROM. Default is to dump the entire EEP- + ROM. + +# ethtool -e eth0 offset 0 length 16 +Offset Values +------ ------ +0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 + +======================== +DEBUGFS COMMANDS +======================== + +those commands are used via debugfs interface + +=========== +rdmac +rdbbp +rdrf + These commands are used to read the MAC, BBP and RF registers from the + card. These commands take one parameter that specifies the offset + location that is to be read. This parameter must be specified in + hexadecimal (its possible to preceed preceding the number with a "0x"). + + Path: /debugfs/libertas_wireless/ethX/registers/ + + Usage: + echo "0xa123" > rdmac ; cat rdmac + echo "0xa123" > rdbbp ; cat rdbbp + echo "0xa123" > rdrf ; cat rdrf +wrmac +wrbbp +wrrf + These commands are used to write the MAC, BBP and RF registers in the + card. These commands take two parameters that specify the offset + location and the value that is to be written. This parameters must + be specified in hexadecimal (its possible to preceed the number + with a "0x"). + + Usage: + echo "0xa123 0xaa" > wrmac + echo "0xa123 0xaa" > wrbbp + echo "0xa123 0xaa" > wrrf + +sleepparams + This command is used to set the sleepclock configurations + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat sleepparams: reads the current sleepclock configuration + + echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + +subscribed_events + + The subscribed_events directory contains the interface for the + subscribed events API. + + Path: /debugfs/libertas_wireless/ethX/subscribed_events/ + + Each event is represented by a filename. Each filename consists of the + following three fields: + Value Frequency Subscribed + + To read the current values for a given event, do: + cat event + To set the current values, do: + echo "60 2 1" > event + + Frequency field specifies the reporting frequency for this event. + If it is set to 0, then the event is reported only once, and then + automatically unsubscribed. If it is set to 1, then the event is + reported every time it occurs. If it is set to N, then the event is + reported every Nth time it occurs. + + beacon_missed + Value field specifies the number of consecutive missing beacons which + triggers the LINK_LOSS event. This event is generated only once after + which the firmware resets its state. At initialization, the LINK_LOSS + event is subscribed by default. The default value of MissedBeacons is + 60. + + failure_count + Value field specifies the consecutive failure count threshold which + triggers the generation of the MAX_FAIL event. Once this event is + generated, the consecutive failure count is reset to 0. + At initialization, the MAX_FAIL event is NOT subscribed by + default. + + high_rssi + This event is generated when the average received RSSI in beacons goes + above a threshold, specified by Value. + + low_rssi + This event is generated when the average received RSSI in beacons goes + below a threshold, specified by Value. + + high_snr + This event is generated when the average received SNR in beacons goes + above a threshold, specified by Value. + + low_snr + This event is generated when the average received SNR in beacons goes + below a threshold, specified by Value. + +extscan + This command is used to do a specific scan. + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: echo "SSID" > extscan + + Example: + echo "LINKSYS-AP" > extscan + + To see the results of use getscantable command. + +getscantable + + Display the current contents of the driver scan table (ie. get the + scan results). + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat getscantable + +setuserscan + Initiate a customized scan and retrieve the results + + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + echo "[ARGS]" > setuserscan + + where [ARGS]: + + chan=[chan#][band][mode] where band is [a,b,g] and mode is + blank for active or 'p' for passive + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + ssid="[SSID]" specify a SSID filter for the scan + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + probes=[#] number of probe requests to send on each chan + type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + + Any combination of the above arguments can be supplied on the command line. + If the chan token is absent, a full channel scan will be completed by + the driver. If the dur or probes tokens are absent, the driver default + setting will be used. The bssid and ssid fields, if blank, + will produce an unfiltered scan. The type field will default to 3 (Any) + and the keep field will default to 0 (Discard). + + Examples: + 1) Perform an active scan on channels 1, 6, and 11 in the 'g' band: + echo "chan=1g,6g,11g" > setuserscan + + 2) Perform a passive scan on channel 11 for 20 ms: + echo "chan=11gp dur=20" > setuserscan + + 3) Perform an active scan on channels 1, 6, and 11; and a passive scan on + channel 36 in the 'a' band: + + echo "chan=1g,6g,11g,36ap" > setuserscan + + 4) Perform an active scan on channel 6 and 36 for a specific SSID: + echo "chan=6g,36a ssid="TestAP"" > setuserscan + + 5) Scan all available channels (B/G, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan + + 6) Scan channel 6, for all infrastructure networks, sending two probe + requests. Keep the previous scan table intact. Update any duplicate + BSSID/SSID matches with the new scan data: + echo "chan=6g type=1 probes=2 keep=1" > setuserscan + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. + +============================================================================== diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c new file mode 100644 index 00000000000..c260bd1b3d4 --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.c @@ -0,0 +1,587 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#include <linux/bitops.h> +#include <net/ieee80211.h> + +#include "assoc.h" +#include "join.h" +#include "decl.h" +#include "hostcmd.h" +#include "host.h" + + +static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static int assoc_helper_essid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "New SSID requested: %s\n", assoc_req->ssid.ssid); + if (assoc_req->mode == IW_MODE_INFRA) { + if (adapter->prescan) { + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1); + } + + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, + NULL, IW_MODE_INFRA); + if (i >= 0) { + lbs_pr_debug(1, + "SSID found in scan list ... associating...\n"); + + ret = wlan_associate(priv, &adapter->scantable[i]); + if (ret == 0) { + memcpy(&assoc_req->bssid, + &adapter->scantable[i].macaddress, + ETH_ALEN); + } + } else { + lbs_pr_debug(1, "SSID '%s' not found; cannot associate\n", + assoc_req->ssid.ssid); + } + } else if (assoc_req->mode == IW_MODE_ADHOC) { + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0); + + /* Search for the requested SSID in the scan table */ + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL, + IW_MODE_ADHOC); + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List, so join\n", ret); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + /* else send START command */ + lbs_pr_debug(1, "SSID not found in list, so creating adhoc" + " with SSID '%s'\n", assoc_req->ssid.ssid); + libertas_start_adhoc_network(priv, &assoc_req->ssid); + } + memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN); + } + + LEAVE(); + return ret; +} + + +static int assoc_helper_bssid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i, ret = 0; + + ENTER(); + + lbs_pr_debug(1, "ASSOC: WAP: BSSID = " MAC_FMT "\n", + MAC_ARG(assoc_req->bssid)); + + /* Search for index position in list for requested MAC */ + i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid, + assoc_req->mode); + if (i < 0) { + lbs_pr_debug(1, "ASSOC: WAP: BSSID " MAC_FMT " not found, " + "cannot associate.\n", MAC_ARG(assoc_req->bssid)); + goto out; + } + + if (assoc_req->mode == IW_MODE_INFRA) { + ret = wlan_associate(priv, &adapter->scantable[i]); + lbs_pr_debug(1, "ASSOC: return from wlan_associate(bssd) was %d\n", ret); + } else if (assoc_req->mode == IW_MODE_ADHOC) { + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } + memcpy(&assoc_req->ssid, &adapter->scantable[i].ssid, + sizeof(struct WLAN_802_11_SSID)); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_associate(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0, done = 0; + + /* If we're given and 'any' BSSID, try associating based on SSID */ + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN) + && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) { + ret = assoc_helper_bssid(priv, assoc_req); + done = 1; + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + } + + if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + ret = assoc_helper_essid(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + + return ret; +} + + +static int assoc_helper_mode(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->mode == adapter->mode) { + LEAVE(); + return 0; + } + + if (assoc_req->mode == IW_MODE_INFRA) { + if (adapter->psstate != PS_STATE_FULL_POWER) + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + adapter->psmode = wlan802_11powermodecam; + } + + adapter->mode = assoc_req->mode; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + 0, cmd_option_waitforrsp, + OID_802_11_INFRASTRUCTURE_MODE, + (void *) (size_t) assoc_req->mode); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wep_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i; + int ret = 0; + + ENTER(); + + /* Set or remove WEP keys */ + if ( assoc_req->wep_keys[0].len + || assoc_req->wep_keys[1].len + || assoc_req->wep_keys[2].len + || assoc_req->wep_keys[3].len) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, assoc_req); + } else { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_remove, + cmd_option_waitforrsp, + 0, NULL); + } + + if (ret) + goto out; + + /* enable/disable the MAC's WEP packet filter */ + if (assoc_req->secinfo.wep_enabled) + adapter->currentpacketfilter |= cmd_act_mac_wep_enable; + else + adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable; + ret = libertas_set_mac_packet_filter(priv); + if (ret) + goto out; + + mutex_lock(&adapter->lock); + + /* Copy WEP keys into adapter wep key fields */ + for (i = 0; i < 4; i++) { + memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx; + + mutex_unlock(&adapter->lock); + +out: + LEAVE(); + return ret; +} + +static int assoc_helper_secinfo(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + memcpy(&adapter->secinfo, &assoc_req->secinfo, + sizeof(struct wlan_802_11_security)); + + ret = libertas_set_mac_packet_filter(priv); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0; + + ENTER(); + + /* enable/Disable RSN */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_enable_rsn, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + if (ret) + goto out; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_key_material, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_ie(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { + memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); + adapter->wpa_ie_len = assoc_req->wpa_ie_len; + } else { + memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN); + adapter->wpa_ie_len = 0; + } + + LEAVE(); + return ret; +} + + +static int should_deauth_infrastructure(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new SSID in " + " configuration request.\n"); + return 1; + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + if (adapter->secinfo.auth_mode != assoc_req->secinfo.auth_mode) { + lbs_pr_debug(1, "Deauthenticating due to updated security " + "info in configuration request.\n"); + return 1; + } + } + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new BSSID in " + " configuration request.\n"); + return 1; + } + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != IW_MODE_INFRA) + return 1; + } + + return 0; +} + + +static int should_stop_adhoc(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength) + return 1; + if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid, + adapter->curbssparams.ssid.ssidlength)) + return 1; + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != IW_MODE_ADHOC) + return 1; + } + + return 0; +} + + +void wlan_association_worker(struct work_struct *work) +{ + wlan_private *priv = container_of(work, wlan_private, assoc_work.work); + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req = NULL; + int ret = 0; + int find_any_ssid = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = adapter->assoc_req; + adapter->assoc_req = NULL; + mutex_unlock(&adapter->lock); + + if (!assoc_req) { + LEAVE(); + return; + } + + lbs_pr_debug(1, "ASSOC: starting new association request: flags = 0x%lX\n", + assoc_req->flags); + + /* If 'any' SSID was specified, find an SSID to associate with */ + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) + && !assoc_req->ssid.ssidlength) + find_any_ssid = 1; + + /* But don't use 'any' SSID if there's a valid locked BSSID to use */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN) + && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN)) + find_any_ssid = 0; + } + + if (find_any_ssid) { + u8 new_mode; + + ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid, + assoc_req->mode, &new_mode); + if (ret) { + lbs_pr_debug(1, "Could not find best network\n"); + ret = -ENETUNREACH; + goto out; + } + + /* Ensure we switch to the mode of the AP */ + if (assoc_req->mode == IW_MODE_AUTO) { + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + assoc_req->mode = new_mode; + } + } + + /* + * Check if the attributes being changing require deauthentication + * from the currently associated infrastructure access point. + */ + if (adapter->mode == IW_MODE_INFRA) { + if (should_deauth_infrastructure(adapter, assoc_req)) { + ret = libertas_send_deauthentication(priv); + if (ret) { + lbs_pr_debug(1, "Deauthentication due to new " + "configuration request failed: %d\n", + ret); + } + } + } else if (adapter->mode == IW_MODE_ADHOC) { + if (should_stop_adhoc(adapter, assoc_req)) { + ret = libertas_stop_adhoc_network(priv); + if (ret) { + lbs_pr_debug(1, "Teardown of AdHoc network due to " + "new configuration request failed: %d\n", + ret); + } + + } + } + + /* Send the various configuration bits to the firmware */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + ret = assoc_helper_mode(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) mode: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { + ret = assoc_helper_wep_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + ret = assoc_helper_secinfo(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + ret = assoc_helper_wpa_ie(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + ret = assoc_helper_wpa_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + /* SSID/BSSID should be the _last_ config option set, because they + * trigger the association attempt. + */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) + || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + int success = 1; + + ret = assoc_helper_associate(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: association attempt unsuccessful: %d\n", + ret); + success = 0; + } + + if (adapter->connect_status != libertas_connected) { + lbs_pr_debug(1, "ASSOC: assoication attempt unsuccessful, " + "not connected.\n"); + success = 0; + } + + if (success) { + lbs_pr_debug(1, "ASSOC: association attempt successful. " + "Associated to '%s' (" MAC_FMT ")\n", + assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid)); + libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, 0, NULL); + + libertas_prepare_and_send_command(priv, + cmd_802_11_get_log, + 0, cmd_option_waitforrsp, 0, NULL); + } else { + + ret = -1; + } + } + +out: + if (ret) { + lbs_pr_debug(1, "ASSOC: reconfiguration attempt unsuccessful: %d\n", + ret); + } + kfree(assoc_req); + LEAVE(); +} + + +/* + * Caller MUST hold any necessary locks + */ +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter) +{ + struct assoc_request * assoc_req; + + if (!adapter->assoc_req) { + adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL); + if (!adapter->assoc_req) { + lbs_pr_info("Not enough memory to allocate association" + " request!\n"); + return NULL; + } + } + + /* Copy current configuration attributes to the association request, + * but don't overwrite any that are already set. + */ + assoc_req = adapter->assoc_req; + if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + } + + if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) + assoc_req->channel = adapter->curbssparams.channel; + + if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) + assoc_req->mode = adapter->mode; + + if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + memcpy(&assoc_req->bssid, adapter->curbssparams.bssid, + ETH_ALEN); + } + + if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { + int i; + for (i = 0; i < 4; i++) { + memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + } + + if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) + assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx; + + if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + memcpy(&assoc_req->secinfo, &adapter->secinfo, + sizeof(struct wlan_802_11_security)); + } + + if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie, + MAX_WPA_IE_LEN); + assoc_req->wpa_ie_len = adapter->wpa_ie_len; + } + + return assoc_req; +} + + diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h new file mode 100644 index 00000000000..2ffd82d99b3 --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#ifndef _WLAN_ASSOC_H_ +#define _WLAN_ASSOC_H_ + +#include "dev.h" + +void wlan_association_worker(struct work_struct *work); + +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter); + +#define ASSOC_DELAY (HZ / 2) +static inline void wlan_postpone_association_work(wlan_private *priv) +{ + if (priv->adapter->surpriseremoved) + return; + cancel_delayed_work(&priv->assoc_work); + queue_delayed_work(priv->assoc_thread, &priv->assoc_work, ASSOC_DELAY); +} + +static inline void wlan_cancel_association_work(wlan_private *priv) +{ + cancel_delayed_work(&priv->assoc_work); + if (priv->adapter->assoc_req) { + kfree(priv->adapter->assoc_req); + priv->adapter->assoc_req = NULL; + } +} + +#endif /* _WLAN_ASSOC_H */ diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c new file mode 100644 index 00000000000..de9cb46a70f --- /dev/null +++ b/drivers/net/wireless/libertas/cmd.c @@ -0,0 +1,1959 @@ +/** + * This file contains the handling of command. + * It prepares command and sends it to firmware when it is ready. + */ + +#include <net/iw_handler.h> +#include "host.h" +#include "hostcmd.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode); + +static u16 commands_allowed_in_ps[] = { + cmd_802_11_rssi, +}; + +/** + * @brief This function checks if the commans is allowed + * in PS mode not. + * + * @param command the command ID + * @return TRUE or FALSE + */ +static u8 is_command_allowed_in_ps(u16 command) +{ + int count = sizeof(commands_allowed_in_ps) + / sizeof(commands_allowed_in_ps[0]); + int i; + + for (i = 0; i < count; i++) { + if (command == cpu_to_le16(commands_allowed_in_ps[i])) + return 1; + } + + return 0; +} + +static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd) +{ + struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_get_hw_spec); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN); + memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_ps_mode(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode; + u16 action = cmd_action; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_ps_mode); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + + S_DS_GEN); + psm->action = cpu_to_le16(cmd_action); + psm->multipledtim = 0; + switch (action) { + case cmd_subcmd_enter_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n"); + lbs_pr_debug(1, "locallisteninterval = %d\n", + adapter->locallisteninterval); + + psm->locallisteninterval = + cpu_to_le16(adapter->locallisteninterval); + psm->nullpktinterval = + cpu_to_le16(adapter->nullpktinterval); + psm->multipledtim = + cpu_to_le16(priv->adapter->multipledtim); + break; + + case cmd_subcmd_exit_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n"); + break; + + case cmd_subcmd_sleep_confirmed: + lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n"); + break; + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + u16 *timeout = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_inactivity_timeout); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout) + + S_DS_GEN); + + cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action); + + if (cmd_action) + cmd->params.inactivity_timeout.timeout = + cpu_to_le16(*timeout); + else + cmd->params.inactivity_timeout.timeout = 0; + + return 0; +} + +static int wlan_cmd_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_sleep_params); + + if (cmd_action == cmd_act_get) { + memset(&adapter->sp, 0, sizeof(struct sleep_params)); + memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params)); + sp->action = cpu_to_le16(cmd_action); + } else if (cmd_action == cmd_act_set) { + sp->action = cpu_to_le16(cmd_action); + sp->error = cpu_to_le16(adapter->sp.sp_error); + sp->offset = cpu_to_le16(adapter->sp.sp_offset); + sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime); + sp->calcontrol = (u8) adapter->sp.sp_calcontrol; + sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk; + sp->reserved = cpu_to_le16(adapter->sp.sp_reserved); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_set_wep(wlan_private * priv, + struct cmd_ds_command *cmd, + u32 cmd_act, + void * pdata_buf) +{ + struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_set_wep); + cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep)) + + S_DS_GEN); + + if (cmd_act == cmd_act_add) { + int i; + + if (!assoc_req) { + lbs_pr_debug(1, "Invalid association request!"); + ret = -1; + goto done; + } + + wep->action = cpu_to_le16(cmd_act_add); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (assoc_req->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + + lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex); + + /* Copy key types and material to host command structure */ + for (i = 0; i < 4; i++) { + struct WLAN_802_11_KEY * pkey = &assoc_req->wep_keys[i]; + + switch (pkey->len) { + case KEY_LEN_WEP_40: + wep->keytype[i] = cmd_type_wep_40_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case KEY_LEN_WEP_104: + wep->keytype[i] = cmd_type_wep_104_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case 0: + break; + default: + lbs_pr_debug(1, "Invalid WEP key %d length of %d\n", + i, pkey->len); + ret = -1; + goto done; + break; + } + } + } else if (cmd_act == cmd_act_remove) { + /* ACT_REMOVE clears _all_ WEP keys */ + wep->action = cpu_to_le16(cmd_act_remove); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (adapter->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + } + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_enable_rsn(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn; + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_enable_rsn); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) + + S_DS_GEN); + penableRSN->action = cpu_to_le16(cmd_action); + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + penableRSN->enable = cpu_to_le16(cmd_enable_rsn); + } else { + penableRSN->enable = cpu_to_le16(cmd_disable_rsn); + } + + return 0; +} + + +static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset, + struct WLAN_802_11_KEY * pkey) +{ + pkeyparamset->keytypeid = cpu_to_le16(pkey->type); + + if (pkey->flags & KEY_INFO_WPA_ENABLED) { + pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED); + } else { + pkeyparamset->keyinfo = cpu_to_le16(!KEY_INFO_WPA_ENABLED); + } + + if (pkey->flags & KEY_INFO_WPA_UNICAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); + } else if (pkey->flags & KEY_INFO_WPA_MCAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); + } + + pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkeyparamset->keylen = cpu_to_le16(pkey->len); + memcpy(pkeyparamset->key, pkey->key, pkey->len); + pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid) + + sizeof(pkeyparamset->keyinfo) + + sizeof(pkeyparamset->keylen) + + sizeof(pkeyparamset->key)); +} + +static int wlan_cmd_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, + u32 cmd_oid, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_key_material *pkeymaterial = + &cmd->params.keymaterial; + int ret = 0; + int index = 0; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_key_material); + pkeymaterial->action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_get) { + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action)); + ret = 0; + goto done; + } + + memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet)); + + if (adapter->wpa_unicast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_unicast_key); + index++; + } + + if (adapter->wpa_mcast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_mcast_key); + index++; + } + + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action) + + index * sizeof(struct MrvlIEtype_keyParamSet)); + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_reset(wlan_private * priv, + struct cmd_ds_command *cmd, int cmd_action) +{ + struct cmd_ds_802_11_reset *reset = &cmd->params.reset; + + cmd->command = cpu_to_le16(cmd_802_11_reset); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN); + reset->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_802_11_get_log(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_log); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_get_stat(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_stat); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, + int cmd_oid, void *pdata_buf) +{ + struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib; + wlan_adapter *adapter = priv->adapter; + u8 ucTemp; + + ENTER(); + + lbs_pr_debug(1, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + + cmd->command = cpu_to_le16(cmd_802_11_snmp_mib); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_snmp_mib) + + S_DS_GEN); + + switch (cmd_oid) { + case OID_802_11_INFRASTRUCTURE_MODE: + { + u8 mode = (u8) (size_t) pdata_buf; + pSNMPMIB->querytype = cpu_to_le16(cmd_act_set); + pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i); + pSNMPMIB->bufsize = sizeof(u8); + if (mode == IW_MODE_ADHOC) { + ucTemp = SNMP_MIB_VALUE_ADHOC; + } else { + /* Infra and Auto modes */ + ucTemp = SNMP_MIB_VALUE_INFRA; + } + + memmove(pSNMPMIB->value, &ucTemp, sizeof(u8)); + + break; + } + + case OID_802_11D_ENABLE: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) dot11d_i); + + if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = cmd_act_set; + pSNMPMIB->bufsize = sizeof(u16); + ulTemp = *(u32 *)pdata_buf; + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + } + break; + } + + case OID_802_11_FRAGMENTATION_THRESHOLD: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) fragthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) pdata_buf); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + + } + + break; + } + + case OID_802_11_RTS_THRESHOLD: + { + + u32 ulTemp; + pSNMPMIB->oid = le16_to_cpu((u16) rtsthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) + pdata_buf); + *(unsigned short *)(pSNMPMIB->value) = + cpu_to_le16((u16) ulTemp); + + } + break; + } + case OID_802_11_TX_RETRYCOUNT: + pSNMPMIB->oid = cpu_to_le16((u16) short_retrylim_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16)); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) adapter->txretrycount); + } + + break; + default: + break; + } + + lbs_pr_debug(1, + "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n", + cmd->command, cmd->size, cmd->seqnum, cmd->result); + + lbs_pr_debug(1, + "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n", + pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize, + *(u16 *) pSNMPMIB->value); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_radio_control(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_radio_control *pradiocontrol = + &cmd->params.radio; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_radio_control); + + pradiocontrol->action = cpu_to_le16(cmd_action); + + switch (adapter->preamble) { + case cmd_type_short_preamble: + pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE); + break; + + case cmd_type_long_preamble: + pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE); + break; + + case cmd_type_auto_preamble: + default: + pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE); + break; + } + + if (adapter->radioon) + pradiocontrol->control |= cpu_to_le16(TURN_ON_RF); + else + pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + + struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rf_tx_power); + prtp->action = cmd_action; + + lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size, + cmd->command, prtp->action); + + switch (cmd_action) { + case cmd_act_tx_power_opt_get: + prtp->action = cpu_to_le16(cmd_act_get); + prtp->currentlevel = 0; + break; + + case cmd_act_tx_power_opt_set_high: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_high); + break; + + case cmd_act_tx_power_opt_set_mid: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_mid); + break; + + case cmd_act_tx_power_opt_set_low: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf)); + break; + } + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant; + + cmd->command = cpu_to_le16(cmd_802_11_rf_antenna); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) + + S_DS_GEN); + + rant->action = cpu_to_le16(cmd_action); + if ((cmd_action == cmd_act_set_rx) || + (cmd_action == cmd_act_set_tx)) { + rant->antennamode = + cpu_to_le16((u16) (*(u32 *) pdata_buf)); + } + + return 0; +} + +static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_rate_adapt_rateset + *rateadapt = &cmd->params.rateset; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rate_adapt_rateset); + + ENTER(); + + rateadapt->action = cmd_action; + rateadapt->enablehwauto = adapter->enablehwauto; + rateadapt->bitmap = adapter->ratebitmap; + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate; + wlan_adapter *adapter = priv->adapter; + u16 action = cmd_action; + + ENTER(); + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) + + S_DS_GEN); + + cmd->command = cpu_to_le16(cmd_802_11_data_rate); + + memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate)); + + pdatarate->action = cpu_to_le16(cmd_action); + + if (action == cmd_act_set_tx_fix_rate) { + pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate); + lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n", + adapter->datarate); + } else if (action == cmd_act_set_tx_auto) { + lbs_pr_debug(1, "Setting FW for AUTO rate\n"); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_mac_multicast_adr(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_mac_multicast_adr); + + pMCastAdr->action = cpu_to_le16(cmd_action); + pMCastAdr->nr_of_adrs = + cpu_to_le16((u16) adapter->nr_of_multicastmacaddr); + memcpy(pMCastAdr->maclist, adapter->multicastlist, + adapter->nr_of_multicastmacaddr * ETH_ALEN); + + return 0; +} + +static int wlan_cmd_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *cmd, + int option, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel; + + cmd->command = cpu_to_le16(cmd_802_11_rf_channel); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel) + + S_DS_GEN); + + if (option == cmd_opt_802_11_rf_channel_set) { + rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf)); + } + + rfchan->action = cpu_to_le16(option); + + return 0; +} + +static int wlan_cmd_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_rssi); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN); + cmd->params.rssi.N = priv->adapter->bcn_avg_factor; + + /* reset Beacon SNR/NF/RSSI values */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_AVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0; + + return 0; +} + +static int wlan_cmd_reg_access(wlan_private * priv, + struct cmd_ds_command *cmdptr, + u8 cmd_action, void *pdata_buf) +{ + struct wlan_offset_value *offval; + + ENTER(); + + offval = (struct wlan_offset_value *)pdata_buf; + + switch (cmdptr->command) { + case cmd_mac_reg_access: + { + struct cmd_ds_mac_reg_access *macreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_mac_reg_access) + + S_DS_GEN); + macreg = + (struct cmd_ds_mac_reg_access *)&cmdptr->params. + macreg; + + macreg->action = cpu_to_le16(cmd_action); + macreg->offset = cpu_to_le16((u16) offval->offset); + macreg->value = cpu_to_le32(offval->value); + + break; + } + + case cmd_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *bbpreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_bbp_reg_access) + + S_DS_GEN); + bbpreg = + (struct cmd_ds_bbp_reg_access *)&cmdptr->params. + bbpreg; + + bbpreg->action = cpu_to_le16(cmd_action); + bbpreg->offset = cpu_to_le16((u16) offval->offset); + bbpreg->value = (u8) offval->value; + + break; + } + + case cmd_rf_reg_access: + { + struct cmd_ds_rf_reg_access *rfreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_rf_reg_access) + + S_DS_GEN); + rfreg = + (struct cmd_ds_rf_reg_access *)&cmdptr->params. + rfreg; + + rfreg->action = cpu_to_le16(cmd_action); + rfreg->offset = cpu_to_le16((u16) offval->offset); + rfreg->value = (u8) offval->value; + + break; + } + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_mac_address); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.macadd.action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_set) { + memcpy(cmd->params.macadd.macadd, + adapter->current_addr, ETH_ALEN); + lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6); + } + + return 0; +} + +static int wlan_cmd_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, void *pdata_buf) +{ + struct wlan_ioctl_regrdwr *ea = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_eeprom_access); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.rdeeprom.action = cpu_to_le16(ea->action); + cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset); + cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB); + cmd->params.rdeeprom.value = 0; + + return 0; +} + +static int wlan_cmd_bt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_bt_access *bt_access = &cmd->params.bt; + lbs_pr_debug(1, "BT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_bt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + + S_DS_GEN); + cmd->result = 0; + bt_access->action = cpu_to_le16(cmd_action); + + switch (cmd_action) { + case cmd_act_bt_access_add: + memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); + lbs_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_del: + memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); + lbs_dbg_hex("BT_DEL: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_list: + bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); + break; + case cmd_act_bt_access_reset: + break; + default: + break; + } + return 0; +} + +static int wlan_cmd_fwt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_fwt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); + else + memset(fwt_access, 0, sizeof(*fwt_access)); + + fwt_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_mesh_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_mesh_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(mesh_access, pdata_buf, sizeof(*mesh_access)); + else + memset(mesh_access, 0, sizeof(*mesh_access)); + + mesh_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + + ENTER(); + + if (!cmdnode) { + lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n"); + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + if (!cmdptr) { + lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n"); + goto done; + } + + /* Exit_PS command needs to be queued in the header always. */ + if (cmdptr->command == cmd_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode; + if (psm->action == cmd_subcmd_exit_ps) { + if (adapter->psstate != PS_STATE_FULL_POWER) + addtail = 0; + } + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (addtail) + list_add_tail((struct list_head *)cmdnode, + &adapter->cmdpendingq); + else + list_add((struct list_head *)cmdnode, &adapter->cmdpendingq); + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "QUEUE_CMD: Inserted node=%p, cmd=0x%x in cmdpendingq\n", + cmdnode, + ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command); + +done: + LEAVE(); + return; +} + +/* + * TODO: Fix the issue when DownloadcommandToStation is being called the + * second time when the command timesout. All the cmdptr->xxx are in little + * endian and therefore all the comparissions will fail. + * For now - we are not performing the endian conversion the second time - but + * for PS and DEEP_SLEEP we need to worry + */ +static int DownloadcommandToStation(wlan_private * priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 cmdsize; + u16 command; + + ENTER(); + + if (!adapter || !cmdnode) { + lbs_pr_debug(1, "DNLD_CMD: adapter = %p, cmdnode = %p\n", + adapter, cmdnode); + if (cmdnode) { + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + } + ret = -1; + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!cmdptr || !cmdptr->size) { + lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, " + "Not sending\n"); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + adapter->cur_cmd = cmdnode; + adapter->cur_cmd_retcode = 0; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n", + cmdptr->size); + + cmdsize = cmdptr->size; + + command = cpu_to_le16(cmdptr->command); + + cmdnode->cmdwaitqwoken = 0; + cmdsize = cpu_to_le16(cmdsize); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize); + + if (ret != 0) { + lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n"); + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies); + lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize); + + /* Setup the timer after transmit command */ + if (command == cmd_802_11_scan + || command == cmd_802_11_authenticate + || command == cmd_802_11_associate) + mod_timer(&adapter->command_timer, jiffies + (10*HZ)); + else + mod_timer(&adapter->command_timer, jiffies + (5*HZ)); + + ret = 0; + + done: + LEAVE(); + return ret; +} + +static int wlan_cmd_mac_control(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + struct cmd_ds_mac_control *mac = &cmd->params.macctrl; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_mac_control); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN); + mac->action = cpu_to_le16(priv->adapter->currentpacketfilter); + + lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n", + mac->action, cmd->size); + + LEAVE(); + return 0; +} + +/** + * This function inserts command node to cmdfreeq + * after cleans it. Requires adapter->driver_lock held. + */ +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + wlan_adapter *adapter = priv->adapter; + + if (!ptempcmd) + goto done; + + cleanup_cmdnode(ptempcmd); + list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq); +done: + return; +} + +void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + +int libertas_set_radio_control(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n", + priv->adapter->radioon, priv->adapter->preamble); + + LEAVE(); + return ret; +} + +int libertas_set_mac_packet_filter(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n", + priv->adapter->currentpacketfilter); + + /* Send MAC control command to station */ + ret = libertas_prepare_and_send_command(priv, + cmd_mac_control, 0, 0, 0, NULL); + + LEAVE(); + return ret; +} + +/** + * @brief This function prepare the command before send to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd_no command number + * @param cmd_action command action: GET or SET + * @param wait_option wait option: wait response or not + * @param cmd_oid cmd oid: treated as sub command + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode; + struct cmd_ds_command *cmdptr; + unsigned long flags; + + ENTER(); + + if (!adapter) { + lbs_pr_debug(1, "PREP_CMD: adapter is Null\n"); + ret = -1; + goto done; + } + + if (adapter->surpriseremoved) { + lbs_pr_debug(1, "PREP_CMD: Card is Removed\n"); + ret = -1; + goto done; + } + + cmdnode = libertas_get_free_cmd_ctrl_node(priv); + + if (cmdnode == NULL) { + lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n"); + + /* Wake up main thread to execute next command */ + wake_up_interruptible(&priv->mainthread.waitq); + ret = -1; + goto done; + } + + libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf); + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr=%p, command=0x%X\n", + cmdptr, cmd_no); + + if (!cmdptr) { + lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + /* Set sequence number, command and INT option */ + adapter->seqnum++; + cmdptr->seqnum = cpu_to_le16(adapter->seqnum); + + cmdptr->command = cmd_no; + cmdptr->result = 0; + + switch (cmd_no) { + case cmd_get_hw_spec: + ret = wlan_cmd_hw_spec(priv, cmdptr); + break; + case cmd_802_11_ps_mode: + ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_scan: + ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf); + break; + + case cmd_mac_control: + ret = wlan_cmd_mac_control(priv, cmdptr); + break; + + case cmd_802_11_associate: + case cmd_802_11_reassociate: + ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_deauthenticate: + ret = libertas_cmd_80211_deauthenticate(priv, cmdptr); + break; + + case cmd_802_11_set_wep: + ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_ad_hoc_start: + ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf); + break; + case cmd_code_dnld: + break; + + case cmd_802_11_reset: + ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_get_log: + ret = wlan_cmd_802_11_get_log(priv, cmdptr); + break; + + case cmd_802_11_authenticate: + ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_get_stat: + ret = wlan_cmd_802_11_get_stat(priv, cmdptr); + break; + + case cmd_802_11_snmp_mib: + ret = wlan_cmd_802_11_snmp_mib(priv, cmdptr, + cmd_action, cmd_oid, pdata_buf); + break; + + case cmd_mac_reg_access: + case cmd_bbp_reg_access: + case cmd_rf_reg_access: + ret = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_channel: + ret = wlan_cmd_802_11_rf_channel(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_tx_power: + ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_radio_control: + ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_rf_antenna: + ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_data_rate: + ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action); + break; + case cmd_802_11_rate_adapt_rateset: + ret = wlan_cmd_802_11_rate_adapt_rateset(priv, + cmdptr, cmd_action); + break; + + case cmd_mac_multicast_adr: + ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_ad_hoc_join: + ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_rssi: + ret = wlan_cmd_802_11_rssi(priv, cmdptr); + break; + + case cmd_802_11_ad_hoc_stop: + ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr); + break; + + case cmd_802_11_enable_rsn: + ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_key_material: + ret = wlan_cmd_802_11_key_material(priv, cmdptr, + cmd_action, cmd_oid, + pdata_buf); + break; + + case cmd_802_11_pairwise_tsc: + break; + case cmd_802_11_group_tsc: + break; + + case cmd_802_11_mac_address: + ret = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_eeprom_access: + ret = wlan_cmd_802_11_eeprom_access(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_set_afc: + case cmd_802_11_get_afc: + + cmdptr->command = cpu_to_le16(cmd_no); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + + S_DS_GEN); + + memmove(&cmdptr->params.afc, + pdata_buf, sizeof(struct cmd_ds_802_11_afc)); + + ret = 0; + goto done; + + case cmd_802_11d_domain_info: + ret = libertas_cmd_802_11d_domain_info(priv, cmdptr, + cmd_no, cmd_action); + break; + + case cmd_802_11_sleep_params: + ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action); + break; + case cmd_802_11_inactivity_timeout: + ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr, + cmd_action, pdata_buf); + libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 0, pdata_buf); + break; + + case cmd_802_11_tpc_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_tpc_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + + S_DS_GEN); + + memmove(&cmdptr->params.tpccfg, + pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); + + ret = 0; + break; + case cmd_802_11_led_gpio_ctrl: + { + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio*) + cmdptr->params.ledgpio.data; + + memmove(&cmdptr->params.ledgpio, + pdata_buf, + sizeof(struct cmd_ds_802_11_led_ctrl)); + + cmdptr->command = + cpu_to_le16(cmd_802_11_led_gpio_ctrl); + +#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8 + cmdptr->size = + cpu_to_le16(gpio->header.len + S_DS_GEN + + ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN); + gpio->header.len = cpu_to_le16(gpio->header.len); + + ret = 0; + break; + } + case cmd_802_11_pwr_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_pwr_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) + + S_DS_GEN); + memmove(&cmdptr->params.pwrcfg, pdata_buf, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + + ret = 0; + break; + case cmd_bt_access: + ret = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_fwt_access: + ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_mesh_access: + ret = wlan_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_get_tsf: + cmdptr->command = cpu_to_le16(cmd_get_tsf); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_get_tsf) + + S_DS_GEN); + ret = 0; + break; + case cmd_802_11_tx_rate_query: + cmdptr->command = + cpu_to_le16(cmd_802_11_tx_rate_query); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_tx_rate_query) + + S_DS_GEN); + adapter->txrate = 0; + ret = 0; + break; + default: + lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no); + ret = -1; + break; + } + + /* return error, since the command preparation failed */ + if (ret != 0) { + lbs_pr_debug(1, "PREP_CMD: command preparation failed\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + cmdnode->cmdwaitqwoken = 0; + + libertas_queue_cmd(adapter, cmdnode, 1); + adapter->nr_cmd_pending++; + wake_up_interruptible(&priv->mainthread.waitq); + + if (wait_option & cmd_option_waitforrsp) { + lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n"); + might_sleep(); + wait_event_interruptible(cmdnode->cmdwait_q, + cmdnode->cmdwaitqwoken); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd_retcode) { + lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n", + adapter->cur_cmd_retcode); + adapter->cur_cmd_retcode = 0; + ret = -1; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function allocates the command buffer and link + * it to command free queue. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_allocate_cmd_buffer(wlan_private * priv) +{ + int ret = 0; + u32 ulbufsize; + u32 i; + struct cmd_ctrl_node *tempcmd_array; + u8 *ptempvirtualaddr; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate and initialize cmdCtrlNode */ + ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; + + if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n"); + ret = -1; + goto done; + } + + adapter->cmd_array = tempcmd_array; + memset(adapter->cmd_array, 0, ulbufsize); + + /* Allocate and initialize command buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n"); + ret = -1; + goto done; + } + + memset(ptempvirtualaddr, 0, ulbufsize); + + /* Update command buffer virtual */ + tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr; + } + + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + init_waitqueue_head(&tempcmd_array[i].cmdwait_q); + libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]); + } + + ret = 0; + done: + LEAVE(); + return ret; +} + +/** + * @brief This function frees the command buffer. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_free_cmd_buffer(wlan_private * priv) +{ + u32 ulbufsize; + unsigned int i; + struct cmd_ctrl_node *tempcmd_array; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* need to check if cmd array is allocated or not */ + if (adapter->cmd_array == NULL) { + lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n"); + goto done; + } + + tempcmd_array = adapter->cmd_array; + + /* Release shared memory buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (tempcmd_array[i].bufvirtualaddr) { + lbs_pr_debug(1, "Free all the array\n"); + kfree(tempcmd_array[i].bufvirtualaddr); + tempcmd_array[i].bufvirtualaddr = NULL; + } + } + + /* Release cmd_ctrl_node */ + if (adapter->cmd_array) { + lbs_pr_debug(1, "Free cmd_array\n"); + kfree(adapter->cmd_array); + adapter->cmd_array = NULL; + } + +done: + LEAVE(); + return 0; +} + +/** + * @brief This function gets a free command node if available in + * command free queue. + * + * @param priv A pointer to wlan_private structure + * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL + */ +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv) +{ + struct cmd_ctrl_node *tempnode; + wlan_adapter *adapter = priv->adapter; + unsigned long flags; + + if (!adapter) + return NULL; + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!list_empty(&adapter->cmdfreeq)) { + tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next; + list_del((struct list_head *)tempnode); + } else { + lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); + tempnode = NULL; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (tempnode) { + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n"); + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n", + tempnode); + cleanup_cmdnode(tempnode); + } + + return tempnode; +} + +/** + * @brief This function cleans command node. + * + * @param ptempnode A pointer to cmdCtrlNode structure + * @return n/a + */ +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode) +{ + if (!ptempnode) + return; + ptempnode->cmdwaitqwoken = 1; + wake_up_interruptible(&ptempnode->cmdwait_q); + ptempnode->status = 0; + ptempnode->cmd_oid = (u32) 0; + ptempnode->wait_option = 0; + ptempnode->pdata_buf = NULL; + + if (ptempnode->bufvirtualaddr != NULL) + memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + return; +} + +/** + * @brief This function initializes the command node. + * + * @param priv A pointer to wlan_private structure + * @param ptempnode A pointer to cmd_ctrl_node structure + * @param cmd_oid cmd oid: treated as sub command + * @param wait_option wait option: wait response or not + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf) +{ + ENTER(); + + if (!ptempnode) + return; + + ptempnode->cmd_oid = cmd_oid; + ptempnode->wait_option = wait_option; + ptempnode->pdata_buf = pdata_buf; + + LEAVE(); +} + +/** + * @brief This function executes next command in command + * pending queue. It will put fimware back to PS mode + * if applicable. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_execute_next_command(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_ds_command *cmdptr; + unsigned long flags; + int ret = 0; + + lbs_pr_debug(1, "libertas_execute_next_command\n"); + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (adapter->cur_cmd) { + lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n"); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + if (!list_empty(&adapter->cmdpendingq)) { + cmdnode = (struct cmd_ctrl_node *) + adapter->cmdpendingq.next; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (cmdnode) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Got next command from cmdpendingq\n"); + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + if (is_command_allowed_in_ps(cmdptr->command)) { + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + ret = -1; + goto done; + } + lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command " + "0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + } else if (adapter->psstate != PS_STATE_FULL_POWER) { + /* + * 1. Non-PS command: + * Queue it. set needtowakeup to TRUE if current state + * is SLEEP, otherwise call libertas_ps_wakeup to send Exit_PS. + * 2. PS command but not Exit_PS: + * Ignore it. + * 3. PS command Exit_PS: + * Set needtowakeup to TRUE if current state is SLEEP, + * otherwise send this command down to firmware + * immediately. + */ + if (cmdptr->command != + cpu_to_le16(cmd_802_11_ps_mode)) { + /* Prepare to send Exit PS, + * this non PS command will be sent later */ + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + /* w/ new scheme, it will not reach here. + since it is blocked in main_thread. */ + adapter->needtowakeup = 1; + } else + libertas_ps_wakeup(priv, 0); + + ret = 0; + goto done; + } else { + /* + * PS command. Ignore it if it is not Exit_PS. + * otherwise send it down immediately. + */ + struct cmd_ds_802_11_ps_mode *psm = + &cmdptr->params.psmode; + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: PS cmd- action=0x%x\n", + psm->action); + if (psm->action != + cpu_to_le16(cmd_subcmd_exit_ps)) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore Enter PS cmd\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + + ret = 0; + goto done; + } + + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + adapter->needtowakeup = 1; + + ret = 0; + goto done; + } + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Sending Exit_PS down...\n"); + } + } + list_del((struct list_head *)cmdnode); + lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n", + cmdptr->command); + DownloadcommandToStation(priv, cmdnode); + } else { + /* + * check if in power save mode, if yes, put the device back + * to PS mode + */ + if ((adapter->psmode != wlan802_11powermodecam) && + (adapter->psstate == PS_STATE_FULL_POWER) && + (adapter->connect_status == libertas_connected)) { + if (adapter->secinfo.WPAenabled + || adapter->secinfo.WPA2enabled) { + /* check for valid WPA group keys */ + if (adapter->wpa_mcast_key.len + || adapter->wpa_unicast_key.len) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: WPA enabled and GTK_SET" + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } else { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: command PendQ is empty," + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } + } + + ret = 0; +done: + return ret; +} + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str) +{ + union iwreq_data iwrq; + u8 buf[50]; + + ENTER(); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; + + /* Send Event to upper layer */ + lbs_pr_debug(1, "Event Indication string = %s\n", + (char *)buf); + lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length); + + lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str); + wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf); + + LEAVE(); + return; +} + +static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size) +{ + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n", + size); + + lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->intcounter || adapter->currenttxskb) + lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n", + adapter->intcounter, adapter->currenttxskb); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (ret) { + lbs_pr_alert( + "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n"); + } else { + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!adapter->intcounter) { + adapter->psstate = PS_STATE_SLEEP; + } else { + lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n", + adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n"); + lbs_pr_debug(1, "+"); + } + + LEAVE(); + return ret; +} + +void libertas_ps_sleep(wlan_private * priv, int wait_option) +{ + + ENTER(); + + /* + * PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_enter_ps, wait_option, 0, NULL); + + LEAVE(); + return; +} + +/** + * @brief This function sends Eixt_PS command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param wait_option wait response or not + * @return n/a + */ +void libertas_ps_wakeup(wlan_private * priv, int wait_option) +{ + enum WLAN_802_11_POWER_MODE Localpsmode; + + ENTER(); + + Localpsmode = wlan802_11powermodecam; + + lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode); + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_exit_ps, + wait_option, 0, &Localpsmode); + + LEAVE(); + return; +} + +/** + * @brief This function checks condition and prepares to + * send sleep confirm command to firmware if ok. + * + * @param priv A pointer to wlan_private structure + * @param psmode Power Saving mode + * @return n/a + */ +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode) +{ + unsigned long flags =0; + wlan_adapter *adapter = priv->adapter; + u8 allowed = 1; + + ENTER(); + + if (priv->wlan_dev.dnld_sent) { + allowed = 0; + lbs_pr_debug(1, "D"); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + allowed = 0; + lbs_pr_debug(1, "C"); + } + if (adapter->intcounter > 0) { + allowed = 0; + lbs_pr_debug(1, "I%d", adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (allowed) { + lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n"); + sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep, + sizeof(struct PS_CMD_ConfirmSleep)); + } else { + lbs_pr_debug(1, "Sleep Confirm has been delayed\n"); + } + + LEAVE(); +} diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c new file mode 100644 index 00000000000..c86454034b5 --- /dev/null +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -0,0 +1,1029 @@ +/** + * This file contains the handling of command + * responses as well as events generated by firmware. + */ +#include <linux/delay.h> +#include <linux/if_arp.h> +#include <linux/netdevice.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +/** + * @brief This function handles disconnect event. it + * reports disconnect to upper layer, clean tx/rx packets, + * reset link state etc. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +void libertas_mac_event_disconnected(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + if (adapter->connect_status != libertas_connected) + return; + + lbs_pr_debug(1, "Handles disconnect event.\n"); + + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* + * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. + * It causes problem in the Supplicant + */ + + msleep_interruptible(1000); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + /* Free Tx and Rx packets */ + kfree_skb(priv->adapter->currenttxskb); + priv->adapter->currenttxskb = NULL; + + /* report disconnect to upper layer */ + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + /* reset SNR/NF/RSSI values */ + memset(adapter->SNR, 0x00, sizeof(adapter->SNR)); + memset(adapter->NF, 0x00, sizeof(adapter->NF)); + memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI)); + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + adapter->rxpd_rate = 0; + lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n", + adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n", + adapter->previousssid.ssid, adapter->previousssid.ssidlength); + + /* reset internal flags */ + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + adapter->wpa_ie_len = 0; + + adapter->connect_status = libertas_disconnected; + + /* + * memorize the previous SSID and BSSID + * it could be used for re-assoc + */ + memcpy(&adapter->previousssid, + &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID)); + memcpy(adapter->previousbssid, + adapter->curbssparams.bssid, ETH_ALEN); + + /* need to erase the current SSID and BSSID info */ + adapter->pattemptedbssdesc = NULL; + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + if (adapter->psstate != PS_STATE_FULL_POWER) { + /* make firmware to exit PS mode */ + lbs_pr_debug(1, "Disconnected, so exit PS mode.\n"); + libertas_ps_wakeup(priv, 0); + } +} + +/** + * @brief This function handles MIC failure event. + * + * @param priv A pointer to wlan_private structure + * @para event the event id + * @return n/a + */ +static void handle_mic_failureevent(wlan_private * priv, u32 event) +{ + char buf[50]; + + memset(buf, 0, sizeof(buf)); + + sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); + + if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { + strcat(buf, "unicast "); + } else { + strcat(buf, "multicast "); + } + + libertas_send_iwevcustom_event(priv, buf); +} + +static int wlan_ret_reg_access(wlan_private * priv, + u16 type, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (type) { + case cmd_ret_mac_reg_access: + { + struct cmd_ds_mac_reg_access *reg; + + reg = + (struct cmd_ds_mac_reg_access *)&resp->params. + macreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *reg; + reg = + (struct cmd_ds_bbp_reg_access *)&resp->params. + bbpreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_rf_reg_access: + { + struct cmd_ds_rf_reg_access *reg; + reg = + (struct cmd_ds_rf_reg_access *)&resp->params. + rfreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + default: + LEAVE(); + return -1; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_get_hw_spec(wlan_private * priv, + struct cmd_ds_command *resp) +{ + u32 i; + struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo); + + adapter->fwreleasenumber = hwspec->fwreleasenumber; + + lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n", + adapter->fwreleasenumber); + lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n", + hwspec->permanentaddr[0], hwspec->permanentaddr[1], + hwspec->permanentaddr[2], hwspec->permanentaddr[3], + hwspec->permanentaddr[4], hwspec->permanentaddr[5]); + lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X version=0x%X\n", + hwspec->hwifversion, hwspec->version); + + adapter->regioncode = le16_to_cpu(hwspec->regioncode); + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (adapter->regioncode == libertas_region_code_to_index[i]) { + adapter->regiontableindex = (u16) i; + break; + } + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + adapter->regioncode = 0x10; + adapter->regiontableindex = 0; + lbs_pr_info( + "unidentified region code, use the default (USA)\n"); + } + + if (adapter->current_addr[0] == 0xff) { + memmove(adapter->current_addr, hwspec->permanentaddr, + ETH_ALEN); + } + + memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + + if (libertas_set_regiontable(priv, adapter->regioncode, 0)) { + ret = -1; + goto done; + } + + if (libertas_set_universaltable(priv, 0)) { + ret = -1; + goto done; + } + + done: + LEAVE(); + return ret; +} + +static int wlan_ret_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n" + " extsleepclk=%x\n", sp->error, sp->offset, + sp->stabletime, sp->calcontrol, sp->externalsleepclk); + adapter->sp.sp_error = le16_to_cpu(sp->error); + adapter->sp.sp_offset = le16_to_cpu(sp->offset); + adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime); + adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol); + adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk); + adapter->sp.sp_reserved = le16_to_cpu(sp->reserved); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_stat(wlan_private * priv, + struct cmd_ds_command *resp) +{ +/* currently adapter->wlan802_11Stat is unused + + struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat; + wlan_adapter *adapter = priv->adapter; + + // TODO Convert it to Big endian befor copy + memcpy(&adapter->wlan802_11Stat, + p11Stat, sizeof(struct cmd_ds_802_11_get_stat)); +*/ + return 0; +} + +static int wlan_ret_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 querytype = le16_to_cpu(smib->querytype); + + ENTER(); + + lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid, + querytype); + lbs_pr_debug(1, "SNMP_RESP: Buf size = %x\n", + le16_to_cpu(smib->bufsize)); + + if (querytype == cmd_act_get) { + switch (oid) { + case fragthresh_i: + priv->adapter->fragthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n", + priv->adapter->fragthsd); + break; + case rtsthresh_i: + priv->adapter->rtsthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n", + priv->adapter->rtsthsd); + break; + case short_retrylim_i: + priv->adapter->txretrycount = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n", + priv->adapter->rtsthsd); + break; + default: + break; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_key_material *pkeymaterial = + &resp->params.keymaterial; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pkeymaterial->action); + + ENTER(); + + /* Copy the returned key to driver private data */ + if (action == cmd_act_get) { + u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet; + u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size)); + + while (buf_ptr < resp_end) { + struct MrvlIEtype_keyParamSet * pkeyparamset = + (struct MrvlIEtype_keyParamSet *) buf_ptr; + struct WLAN_802_11_KEY * pkey; + u16 key_info = le16_to_cpu(pkeyparamset->keyinfo); + u16 param_set_len = le16_to_cpu(pkeyparamset->length); + u8 * end; + u16 key_len = le16_to_cpu(pkeyparamset->keylen); + + end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type) + + sizeof (pkeyparamset->length) + + param_set_len; + /* Make sure we don't access past the end of the IEs */ + if (end > resp_end) + break; + + if (key_info & KEY_INFO_WPA_UNICAST) + pkey = &adapter->wpa_unicast_key; + else if (key_info & KEY_INFO_WPA_MCAST) + pkey = &adapter->wpa_mcast_key; + else + break; + + /* Copy returned key into driver */ + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + if (key_len > sizeof(pkey->key)) + break; + pkey->type = le16_to_cpu(pkeyparamset->keytypeid); + pkey->flags = le16_to_cpu(pkeyparamset->keyinfo); + pkey->len = le16_to_cpu(pkeyparamset->keylen); + memcpy(pkey->key, pkeyparamset->key, pkey->len); + + buf_ptr = end + 1; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel); + + lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pAntenna->action); + + if (action == cmd_act_get_rx) + adapter->rxantennamode = + le16_to_cpu(pAntenna->antennamode); + + if (action == cmd_act_get_tx) + adapter->txantennamode = + le16_to_cpu(pAntenna->antennamode); + + lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n", + action, le16_to_cpu(pAntenna->antennamode)); + + return 0; +} + +static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rate_adapt_rateset *rates = + &resp->params.rateset; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (rates->action == cmd_act_get) { + adapter->enablehwauto = rates->enablehwauto; + adapter->ratebitmap = rates->bitmap; + } + + LEAVE(); + + return 0; +} + +static int wlan_ret_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate; + wlan_adapter *adapter = priv->adapter; + u8 dot11datarate; + + ENTER(); + + lbs_dbg_hex("DATA_RATE_RESP: data_rate- ", + (u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate)); + + dot11datarate = pdatarate->datarate[0]; + if (pdatarate->action == cmd_act_get_tx_rate) { + memcpy(adapter->libertas_supported_rates, pdatarate->datarate, + sizeof(adapter->libertas_supported_rates)); + } + adapter->datarate = libertas_index_to_data_rate(dot11datarate); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_channel *rfchannel = + &resp->params.rfchannel; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(rfchannel->action); + u16 newchannel = le16_to_cpu(rfchannel->currentchannel); + + ENTER(); + + if (action == cmd_opt_802_11_rf_channel_get + && adapter->curbssparams.channel != newchannel) { + lbs_pr_debug(1, "channel Switch: %d to %d\n", + adapter->curbssparams.channel, newchannel); + + /* Update the channel again */ + adapter->curbssparams.channel = newchannel; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; + wlan_adapter *adapter = priv->adapter; + + /* store the non average value */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR); + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = + le16_to_cpu(rssirsp->noisefloor); + + adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR); + adapter->NF[TYPE_BEACON][TYPE_AVG] = + le16_to_cpu(rssirsp->avgnoisefloor); + + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); + + lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n", + adapter->RSSI[TYPE_BEACON][TYPE_AVG]); + + return 0; +} + +static int wlan_ret_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr *pbuf; + pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom; + + lbs_pr_debug(1, "eeprom read len=%x\n", + le16_to_cpu(resp->params.rdeeprom.bytecount)); + if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) { + pbuf->NOB = 0; + lbs_pr_debug(1, "eeprom read return length is too big\n"); + return -1; + } + pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount); + if (pbuf->NOB > 0) { + + memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + lbs_dbg_hex("adapter", (char *)&pbuf->value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + } + return 0; +} + +static int wlan_ret_get_log(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_get_log *logmessage = + (struct cmd_ds_802_11_get_log *)&resp->params.glog; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* TODO Convert it to Big Endian before copy */ + memcpy(&adapter->logmsg, logmessage, + sizeof(struct cmd_ds_802_11_get_log)); + + LEAVE(); + return 0; +} + +static inline int handle_cmd_response(u16 respcmd, + struct cmd_ds_command *resp, + wlan_private *priv) +{ + int ret = 0; + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + + switch (respcmd) { + case cmd_ret_mac_reg_access: + case cmd_ret_bbp_reg_access: + case cmd_ret_rf_reg_access: + ret = wlan_ret_reg_access(priv, respcmd, resp); + break; + + case cmd_ret_hw_spec_info: + ret = wlan_ret_get_hw_spec(priv, resp); + break; + + case cmd_ret_802_11_scan: + ret = libertas_ret_80211_scan(priv, resp); + break; + + case cmd_ret_802_11_get_log: + ret = wlan_ret_get_log(priv, resp); + break; + + case cmd_ret_802_11_associate: + case cmd_ret_802_11_reassociate: + ret = libertas_ret_80211_associate(priv, resp); + break; + + case cmd_ret_802_11_disassociate: + case cmd_ret_802_11_deauthenticate: + ret = libertas_ret_80211_disassociate(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_start: + case cmd_ret_802_11_ad_hoc_join: + ret = libertas_ret_80211_ad_hoc_start(priv, resp); + break; + + case cmd_ret_802_11_stat: + ret = wlan_ret_802_11_stat(priv, resp); + break; + + case cmd_ret_802_11_snmp_mib: + ret = wlan_ret_802_11_snmp_mib(priv, resp); + break; + + case cmd_ret_802_11_rf_tx_power: + ret = wlan_ret_802_11_rf_tx_power(priv, resp); + break; + + case cmd_ret_802_11_set_afc: + case cmd_ret_802_11_get_afc: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.afc, + sizeof(struct cmd_ds_802_11_afc)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + case cmd_ret_802_11_rf_antenna: + ret = wlan_ret_802_11_rf_antenna(priv, resp); + break; + + case cmd_ret_mac_multicast_adr: + case cmd_ret_mac_control: + case cmd_ret_802_11_set_wep: + case cmd_ret_802_11_reset: + case cmd_ret_802_11_authenticate: + case cmd_ret_802_11_radio_control: + case cmd_ret_802_11_beacon_stop: + case cmd_ret_802_11_enable_rsn: + break; + + case cmd_ret_802_11_data_rate: + ret = wlan_ret_802_11_data_rate(priv, resp); + break; + case cmd_ret_802_11_rate_adapt_rateset: + ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp); + break; + case cmd_ret_802_11_rf_channel: + ret = wlan_ret_802_11_rf_channel(priv, resp); + break; + + case cmd_ret_802_11_rssi: + ret = wlan_ret_802_11_rssi(priv, resp); + break; + + case cmd_ret_802_11_mac_address: + ret = wlan_ret_802_11_mac_address(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_stop: + ret = libertas_ret_80211_ad_hoc_stop(priv, resp); + break; + + case cmd_ret_802_11_key_material: + lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n"); + ret = wlan_ret_802_11_key_material(priv, resp); + break; + + case cmd_ret_802_11_eeprom_access: + ret = wlan_ret_802_11_eeprom_access(priv, resp); + break; + + case cmd_ret_802_11d_domain_info: + ret = libertas_ret_802_11d_domain_info(priv, resp); + break; + + case cmd_ret_802_11_sleep_params: + ret = wlan_ret_802_11_sleep_params(priv, resp); + break; + case cmd_ret_802_11_inactivity_timeout: + spin_lock_irqsave(&adapter->driver_lock, flags); + *((u16 *) adapter->cur_cmd->pdata_buf) = + le16_to_cpu(resp->params.inactivity_timeout.timeout); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + + case cmd_ret_802_11_tpc_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.tpccfg, + sizeof(struct cmd_ds_802_11_tpc_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_led_gpio_ctrl: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.ledgpio, + sizeof(struct cmd_ds_802_11_led_ctrl)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_pwr_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.pwrcfg, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + + case cmd_ret_get_tsf: + spin_lock_irqsave(&adapter->driver_lock, flags); + memcpy(priv->adapter->cur_cmd->pdata_buf, + &resp->params.gettsf.tsfvalue, sizeof(u64)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_bt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.bt.addr1, 2 * ETH_ALEN); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_fwt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.fwt, + sizeof(resp->params.fwt)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_mesh_access: + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.mesh, + sizeof(resp->params.mesh)); + break; + case cmd_rte_802_11_tx_rate_query: + priv->adapter->txrate = resp->params.txrate.txrate; + break; + default: + lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + return ret; +} + +int libertas_process_rx_command(wlan_private * priv) +{ + u16 respcmd; + struct cmd_ds_command *resp; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + ulong flags; + u16 result; + + ENTER(); + + lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies); + + /* Now we got response from FW, cancel the command timer */ + del_timer(&adapter->command_timer); + + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!adapter->cur_cmd) { + lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd); + ret = -1; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + goto done; + } + resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr); + + lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr, + priv->wlan_dev.upld_len); + + respcmd = le16_to_cpu(resp->command); + + result = le16_to_cpu(resp->result); + + lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd, + result, priv->wlan_dev.upld_len); + + if (!(respcmd & 0x8000)) { + lbs_pr_debug(1, "Invalid response to command!"); + adapter->cur_cmd_retcode = -1; + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + /* Store the response code to cur_cmd_retcode. */ + adapter->cur_cmd_retcode = le16_to_cpu(resp->result); + + if (respcmd == cmd_ret_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psmode; + + psmode = &resp->params.psmode; + lbs_pr_debug(1, + "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n", + resp->result, psmode->action); + psmode->action = cpu_to_le16(psmode->action); + + if (result) { + lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n", + resp->result); + if (adapter->mode == IW_MODE_ADHOC) { + /* + * We should not re-try enter-ps command in + * ad-hoc mode. It takes place in + * libertas_execute_next_command(). + */ + if (psmode->action == cmd_subcmd_enter_ps) + adapter->psmode = + wlan802_11powermodecam; + } + } else if (psmode->action == cmd_subcmd_enter_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_AWAKE; + + lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n"); + if (adapter->connect_status != libertas_connected) { + /* + * When Deauth Event received before Enter_PS command + * response, We need to wake up the firmware. + */ + lbs_pr_debug(1, + "Disconnected, Going to invoke libertas_ps_wakeup\n"); + + mutex_unlock(&adapter->lock); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + libertas_ps_wakeup(priv, 0); + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + } + } else if (psmode->action == cmd_subcmd_exit_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_FULL_POWER; + lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n"); + } else { + lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n", + psmode->action); + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = 0; + goto done; + } + + if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) { + /* Copy the response back to response buffer */ + memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size); + + adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD; + } + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n", + resp->command, resp->result); + /* + * Handling errors here + */ + switch (respcmd) { + case cmd_ret_hw_spec_info: + case cmd_ret_802_11_reset: + lbs_pr_debug(1, "CMD_RESP: Reset command failed\n"); + break; + + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = handle_cmd_response(respcmd, resp, priv); + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + WARN_ON(adapter->nr_cmd_pending > 128); + adapter->cur_cmd = NULL; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + mutex_unlock(&adapter->lock); + LEAVE(); + return ret; +} + +int libertas_process_event(wlan_private * priv) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + u32 eventcause; + + spin_lock_irq(&adapter->driver_lock); + eventcause = adapter->eventcause; + spin_unlock_irq(&adapter->driver_lock); + + ENTER(); + + lbs_pr_debug(1, "EVENT Cause %x\n", eventcause); + + switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) { + case MACREG_INT_CODE_LINK_SENSED: + lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n"); + break; + + case MACREG_INT_CODE_DEAUTHENTICATED: + lbs_pr_debug(1, "EVENT: Deauthenticated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_DISASSOCIATED: + lbs_pr_debug(1, "EVENT: Disassociated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_LINK_LOSE_NO_SCAN: + lbs_pr_debug(1, "EVENT: Link lost\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_PS_SLEEP: + lbs_pr_debug(1, "EVENT: SLEEP\n"); + lbs_pr_debug(1, "_"); + + /* handle unexpected PS SLEEP event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS SLEEP\n"); + break; + } + adapter->psstate = PS_STATE_PRE_SLEEP; + + libertas_ps_confirm_sleep(priv, (u16) adapter->psmode); + + break; + + case MACREG_INT_CODE_PS_AWAKE: + lbs_pr_debug(1, "EVENT: AWAKE \n"); + lbs_pr_debug(1, "|"); + + /* handle unexpected PS AWAKE event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); + break; + } + + adapter->psstate = PS_STATE_AWAKE; + + if (adapter->needtowakeup) { + /* + * wait for the command processing to finish + * before resuming sending + * adapter->needtowakeup will be set to FALSE + * in libertas_ps_wakeup() + */ + lbs_pr_debug(1, "Waking up...\n"); + libertas_ps_wakeup(priv, 0); + } + break; + + case MACREG_INT_CODE_MIC_ERR_UNICAST: + lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); + break; + + case MACREG_INT_CODE_MIC_ERR_MULTICAST: + lbs_pr_debug(1, "EVENT: MULTICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); + break; + case MACREG_INT_CODE_MIB_CHANGED: + case MACREG_INT_CODE_INIT_DONE: + break; + + case MACREG_INT_CODE_ADHOC_BCN_LOST: + lbs_pr_debug(1, "EVENT: HWAC - ADHOC BCN LOST\n"); + break; + + case MACREG_INT_CODE_RSSI_LOW: + lbs_pr_alert( "EVENT: RSSI_LOW\n"); + break; + case MACREG_INT_CODE_SNR_LOW: + lbs_pr_alert( "EVENT: SNR_LOW\n"); + break; + case MACREG_INT_CODE_MAX_FAIL: + lbs_pr_alert( "EVENT: MAX_FAIL\n"); + break; + case MACREG_INT_CODE_RSSI_HIGH: + lbs_pr_alert( "EVENT: RSSI_HIGH\n"); + break; + case MACREG_INT_CODE_SNR_HIGH: + lbs_pr_alert( "EVENT: SNR_HIGH\n"); + break; + + default: + lbs_pr_alert( "EVENT: unknown event id: %#x\n", + eventcause >> SBI_EVENT_CAUSE_SHIFT); + break; + } + + spin_lock_irq(&adapter->driver_lock); + adapter->eventcause = 0; + spin_unlock_irq(&adapter->driver_lock); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c new file mode 100644 index 00000000000..7d7bc5e86a5 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.c @@ -0,0 +1,1930 @@ +#include <linux/module.h> +#include <linux/dcache.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <net/iw_handler.h> +#include "dev.h" +#include "decl.h" +#include "host.h" +#include "debugfs.h" + +static struct dentry *libertas_dir = NULL; +static char *szStates[] = { + "Connected", + "Disconnected" +}; + +void libertas_debug_init(wlan_private * priv, struct net_device *dev); + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static const size_t len = PAGE_SIZE; + +static ssize_t libertas_dev_info(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + ssize_t res; + + pos += snprintf(buf+pos, len-pos, "state = %s\n", + szStates[priv->adapter->connect_status]); + pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", + (u32) priv->adapter->regioncode); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + + +static ssize_t libertas_getscantable(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + int numscansdone = 0, res; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + pos += snprintf(buf+pos, len-pos, + "# | ch | ss | bssid | cap | TSF | Qual | SSID \n"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + + while (numscansdone < priv->adapter->numinscantable) { + struct bss_descriptor *pbssinfo; + u16 cap; + + pbssinfo = &priv->adapter->scantable[numscansdone]; + memcpy(&cap, &pbssinfo->cap, sizeof(cap)); + pos += snprintf(buf+pos, len-pos, + "%02u| %03d | %03ld | %02x:%02x:%02x:%02x:%02x:%02x |", + numscansdone, pbssinfo->channel, pbssinfo->rssi, + pbssinfo->macaddress[0], pbssinfo->macaddress[1], + pbssinfo->macaddress[2], pbssinfo->macaddress[3], + pbssinfo->macaddress[4], pbssinfo->macaddress[5]); + pos += snprintf(buf+pos, len-pos, " %04x-", cap); + pos += snprintf(buf+pos, len-pos, "%c%c%c |", + pbssinfo->cap.ibss ? 'A' : 'I', + pbssinfo->cap.privacy ? 'P' : ' ', + pbssinfo->cap.spectrummgmt ? 'S' : ' '); + pos += snprintf(buf+pos, len-pos, " %08llx |", pbssinfo->networktsf); + pos += snprintf(buf+pos, len-pos, " %d |", + SCAN_RSSI(priv->adapter->scantable[numscansdone].rssi)); + + pos += snprintf(buf+pos, len-pos, " %s\n", pbssinfo->ssid.ssid); + + numscansdone++; + } + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t buf_size, res; + int p1, p2, p3, p4, p5, p6; + struct sleep_params sp; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); + if (res != 6) { + res = -EFAULT; + goto out_unlock; + } + sp.sp_error = p1; + sp.sp_offset = p2; + sp.sp_stabletime = p3; + sp.sp_calcontrol = p4; + sp.sp_extsleepclk = p5; + sp.sp_reserved = p6; + + memcpy(&priv->adapter->sp, &sp, sizeof(struct sleep_params)); + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (!res) + res = count; + else + res = -EINVAL; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_get, + cmd_option_waitforrsp, 0, NULL); + if (res) { + res = -EFAULT; + goto out_unlock; + } + + pos += snprintf(buf, len, "%d %d %d %d %d %d\n", adapter->sp.sp_error, + adapter->sp.sp_offset, adapter->sp.sp_stabletime, + adapter->sp.sp_calcontrol, adapter->sp.sp_extsleepclk, + adapter->sp.sp_reserved); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_extscan(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct WLAN_802_11_SSID extscan_ssid; + union iwreq_data wrqu; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + memcpy(&extscan_ssid.ssid, buf, strlen(buf)-1); + extscan_ssid.ssidlength = strlen(buf)-1; + + libertas_send_specific_SSID_scan(priv, &extscan_ssid, 1); + + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + return count; +} + +static int libertas_parse_chan(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg, int dur) +{ + char *start, *end, *hold, *str; + int i = 0; + + start = strstr(buf, "chan="); + if (!start) + return -EINVAL; + start += 5; + end = strstr(start, " "); + if (!end) + end = buf + count; + hold = kzalloc((end - start)+1, GFP_KERNEL); + if (!hold) + return -ENOMEM; + strncpy(hold, start, end - start); + hold[(end-start)+1] = '\0'; + while(hold && (str = strsep(&hold, ","))) { + int chan; + char band, passive = 0; + sscanf(str, "%d%c%c", &chan, &band, &passive); + scan_cfg->chanlist[i].channumber = chan; + scan_cfg->chanlist[i].scantype = passive ? 1 : 0; + if (band == 'b' || band == 'g') + scan_cfg->chanlist[i].radiotype = 0; + else if (band == 'a') + scan_cfg->chanlist[i].radiotype = 1; + + scan_cfg->chanlist[i].scantime = dur; + i++; + } + + kfree(hold); + return i; +} + +static void libertas_parse_bssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + unsigned int mac[ETH_ALEN]; + int i; + + hold = strstr(buf, "bssid="); + if (!hold) + return; + hold += 6; + sscanf(hold, "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3, + mac+4, mac+5); + for(i=0;i<ETH_ALEN;i++) + scan_cfg->specificBSSID[i] = mac[i]; +} + +static void libertas_parse_ssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold, *end; + ssize_t size; + + hold = strstr(buf, "ssid="); + if (!hold) + return; + hold += 5; + end = strstr(hold, " "); + if (!end) + end = buf + count - 1; + + size = min((size_t)IW_ESSID_MAX_SIZE, (size_t) (end - hold)); + strncpy(scan_cfg->specificSSID, hold, size); + + return; +} + +static void libertas_parse_keep(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "keep="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + if (val != 0) + val = 1; + + scan_cfg->keeppreviousscan = val; + return; +} + +static int libertas_parse_dur(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "dur="); + if (!hold) + return 0; + hold += 4; + sscanf(hold, "%d", &val); + + return val; +} + +static void libertas_parse_probes(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "probes="); + if (!hold) + return; + hold += 7; + sscanf(hold, "%d", &val); + + scan_cfg->numprobes = val; + + return; +} + +static void libertas_parse_type(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "type="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + /* type=1,2 or 3 */ + if (val < 1 || val > 3) + return; + + scan_cfg->bsstype = val; + + return; +} + +static ssize_t libertas_setuserscan(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct wlan_ioctl_user_scan_cfg *scan_cfg; + union iwreq_data wrqu; + int dur; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + scan_cfg = kzalloc(sizeof(struct wlan_ioctl_user_scan_cfg), GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + scan_cfg->bsstype = WLAN_SCAN_BSS_TYPE_ANY; + + dur = libertas_parse_dur(buf, count, scan_cfg); + libertas_parse_chan(buf, count, scan_cfg, dur); + libertas_parse_bssid(buf, count, scan_cfg); + libertas_parse_ssid(buf, count, scan_cfg); + libertas_parse_keep(buf, count, scan_cfg); + libertas_parse_probes(buf, count, scan_cfg); + libertas_parse_type(buf, count, scan_cfg); + + wlan_scan_networks(priv, scan_cfg); + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + memset(&wrqu, 0x00, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + kfree(scan_cfg); + return count; +} + +static int libertas_event_initcmd(wlan_private *priv, void **response_buf, + struct cmd_ctrl_node **cmdnode, + struct cmd_ds_command **cmd) +{ + u16 wait_option = cmd_option_waitforrsp; + + if (!(*cmdnode = libertas_get_free_cmd_ctrl_node(priv))) { + lbs_pr_debug(1, "failed libertas_get_free_cmd_ctrl_node\n"); + return -ENOMEM; + } + if (!(*response_buf = kmalloc(3000, GFP_KERNEL))) { + lbs_pr_debug(1, "failed to allocate response buffer!\n"); + return -ENOMEM; + } + libertas_set_cmd_ctrl_node(priv, *cmdnode, 0, wait_option, NULL); + init_waitqueue_head(&(*cmdnode)->cmdwait_q); + (*cmdnode)->pdata_buf = *response_buf; + (*cmdnode)->cmdflags |= CMD_F_HOSTCMD; + (*cmdnode)->cmdwaitqwoken = 0; + *cmd = (struct cmd_ds_command *)(*cmdnode)->bufvirtualaddr; + (*cmd)->command = cmd_802_11_subscribe_event; + (*cmd)->seqnum = ++priv->adapter->seqnum; + (*cmd)->result = 0; + return 0; +} + +static ssize_t libertas_lowrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Lowrssi; + case TLV_TYPE_RSSI_LOW: + Lowrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Lowrssi->rssivalue, + Lowrssi->rssifreq, + (event->events & 0x0001)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static u16 libertas_get_events_bitmap(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res; + u16 event_bitmap; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + return res; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + event_bitmap = event->events; + kfree(response_buf); + return event_bitmap; +} + +static ssize_t libertas_lowrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(0x0104); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0001 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *LowSnr; + case TLV_TYPE_SNR_LOW: + LowSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + LowSnr->snrvalue, + LowSnr->snrfreq, + (event->events & 0x0002)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0002 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_failurecount *failcount; + case TLV_TYPE_FAILCOUNT: + failcount = (struct mrvlietypes_failurecount *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + failcount->failvalue, + failcount->Failfreq, + (event->events & 0x0004)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_failurecount); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_failurecount *failcount; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_failurecount)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + failcount = (struct mrvlietypes_failurecount *)(ptr); + failcount->header.type = cpu_to_le16(TLV_TYPE_FAILCOUNT); + failcount->header.len = 2; + failcount->failvalue = cpu_to_le16(value); + failcount->Failfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0004 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = (struct cmd_ds_command *)response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + free_page(addr); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_beaconsmissed *bcnmiss; + case TLV_TYPE_BCNMISS: + bcnmiss = (struct mrvlietypes_beaconsmissed *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d N/A %d\n", + bcnmiss->beaconmissed, + (event->events & 0x0008)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_beaconsmissed); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_beaconsmissed *bcnmiss; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_beaconsmissed)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + bcnmiss = (struct mrvlietypes_beaconsmissed *)(ptr); + bcnmiss->header.type = cpu_to_le16(TLV_TYPE_BCNMISS); + bcnmiss->header.len = 2; + bcnmiss->beaconmissed = cpu_to_le16(value); + event_bitmap |= subscribed ? 0x0008 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Highrssi; + case TLV_TYPE_RSSI_HIGH: + Highrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Highrssi->rssivalue, + Highrssi->rssifreq, + (event->events & 0x0010)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0010 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *HighSnr; + case TLV_TYPE_SNR_HIGH: + HighSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + HighSnr->snrvalue, + HighSnr->snrfreq, + (event->events & 0x0020)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_HIGH); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0020 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdmac_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->mac_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", + priv->mac_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return ret; +} + +static ssize_t libertas_rdmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdbbp_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->bbp_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", + priv->bbp_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdrf_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->rf_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", + priv->rf_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->rf_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +#define FOPS(fread, fwrite) { \ + .owner = THIS_MODULE, \ + .open = open_file_generic, \ + .read = (fread), \ + .write = (fwrite), \ +} + +struct libertas_debugfs_files { + char *name; + int perm; + struct file_operations fops; +}; + +static struct libertas_debugfs_files debugfs_files[] = { + { "info", 0444, FOPS(libertas_dev_info, write_file_dummy), }, + { "getscantable", 0444, FOPS(libertas_getscantable, + write_file_dummy), }, + { "sleepparams", 0644, FOPS(libertas_sleepparams_read, + libertas_sleepparams_write), }, + { "extscan", 0600, FOPS(NULL, libertas_extscan), }, + { "setuserscan", 0600, FOPS(NULL, libertas_setuserscan), }, +}; + +static struct libertas_debugfs_files debugfs_events_files[] = { + {"low_rssi", 0644, FOPS(libertas_lowrssi_read, + libertas_lowrssi_write), }, + {"low_snr", 0644, FOPS(libertas_lowsnr_read, + libertas_lowsnr_write), }, + {"failure_count", 0644, FOPS(libertas_failcount_read, + libertas_failcount_write), }, + {"beacon_missed", 0644, FOPS(libertas_bcnmiss_read, + libertas_bcnmiss_write), }, + {"high_rssi", 0644, FOPS(libertas_highrssi_read, + libertas_highrssi_write), }, + {"high_snr", 0644, FOPS(libertas_highsnr_read, + libertas_highsnr_write), }, +}; + +static struct libertas_debugfs_files debugfs_regs_files[] = { + {"rdmac", 0644, FOPS(libertas_rdmac_read, libertas_rdmac_write), }, + {"wrmac", 0600, FOPS(NULL, libertas_wrmac_write), }, + {"rdbbp", 0644, FOPS(libertas_rdbbp_read, libertas_rdbbp_write), }, + {"wrbbp", 0600, FOPS(NULL, libertas_wrbbp_write), }, + {"rdrf", 0644, FOPS(libertas_rdrf_read, libertas_rdrf_write), }, + {"wrrf", 0600, FOPS(NULL, libertas_wrrf_write), }, +}; + +void libertas_debugfs_init(void) +{ + if (!libertas_dir) + libertas_dir = debugfs_create_dir("libertas_wireless", NULL); + + return; +} + +void libertas_debugfs_remove(void) +{ + if (libertas_dir) + debugfs_remove(libertas_dir); + return; +} + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev) +{ + int i; + struct libertas_debugfs_files *files; + if (!libertas_dir) + goto exit; + + priv->debugfs_dir = debugfs_create_dir(dev->name, libertas_dir); + if (!priv->debugfs_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_files); i++) { + files = &debugfs_files[i]; + priv->debugfs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->debugfs_dir, + priv, + &files->fops); + } + + priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); + if (!priv->events_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) { + files = &debugfs_events_files[i]; + priv->debugfs_events_files[i] = debugfs_create_file(files->name, + files->perm, + priv->events_dir, + priv, + &files->fops); + } + + priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); + if (!priv->regs_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) { + files = &debugfs_regs_files[i]; + priv->debugfs_regs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->regs_dir, + priv, + &files->fops); + } + +#ifdef PROC_DEBUG + libertas_debug_init(priv, dev); +#endif +exit: + return; +} + +void libertas_debugfs_remove_one(wlan_private *priv) +{ + int i; + + for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) + debugfs_remove(priv->debugfs_regs_files[i]); + + debugfs_remove(priv->regs_dir); + + for(i=0; i<ARRAY_SIZE(debugfs_files); i++) + debugfs_remove(priv->debugfs_events_files[i]); + + debugfs_remove(priv->events_dir); +#ifdef PROC_DEBUG + debugfs_remove(priv->debugfs_debug); +#endif + for(i=0; i<ARRAY_SIZE(debugfs_files); i++) + debugfs_remove(priv->debugfs_files[i]); +} + +/* debug entry */ + +#define item_size(n) (FIELD_SIZEOF(wlan_adapter, n)) +#define item_addr(n) (offsetof(wlan_adapter, n)) + +struct debug_data { + char name[32]; + u32 size; + size_t addr; +}; + +/* To debug any member of wlan_adapter, simply add one line here. + */ +static struct debug_data items[] = { + {"intcounter", item_size(intcounter), item_addr(intcounter)}, + {"psmode", item_size(psmode), item_addr(psmode)}, + {"psstate", item_size(psstate), item_addr(psstate)}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/** + * @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 ssize_t wlan_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int val = 0; + size_t pos = 0; + ssize_t res; + char *p; + int i; + struct debug_data *d; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + p = buf; + + d = (struct debug_data *)file->private_data; + + 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); + else if (d[i].size == 8) + val = *((u64 *) d[i].addr); + + pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); + } + + res = simple_read_from_buffer(userbuf, count, ppos, p, pos); + + free_page(addr); + return res; +} + +/** + * @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 wlan_debugfs_write(struct file *f, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = (struct debug_data *)f->private_data; + + pdata = (char *)kmalloc(cnt, GFP_KERNEL); + if (pdata == NULL) + return 0; + + if (copy_from_user(pdata, buf, cnt)) { + lbs_pr_debug(1, "Copy from user failed\n"); + kfree(pdata); + 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 = simple_strtoul(p2, NULL, 0); + 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; + else if (d[i].size == 8) + *((u64 *) d[i].addr) = (u64) r; + break; + } while (1); + } + kfree(pdata); + + return (ssize_t)cnt; +} + +static struct file_operations libertas_debug_fops = { + .owner = THIS_MODULE, + .open = open_file_generic, + .write = wlan_debugfs_write, + .read = wlan_debugfs_read, +}; + +/** + * @brief create debug proc file + * + * @param priv pointer wlan_private + * @param dev pointer net_device + * @return N/A + */ +void libertas_debug_init(wlan_private * priv, struct net_device *dev) +{ + int i; + + if (!priv->debugfs_dir) + return; + + for (i = 0; i < num_of_items; i++) + items[i].addr += (size_t) priv->adapter; + + priv->debugfs_debug = debugfs_create_file("debug", 0644, + priv->debugfs_dir, &items[0], + &libertas_debug_fops); +} + diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h new file mode 100644 index 00000000000..880a11b95d2 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.h @@ -0,0 +1,6 @@ +void libertas_debugfs_init(void); +void libertas_debugfs_remove(void); + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev); +void libertas_debugfs_remove_one(wlan_private *priv); + diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h new file mode 100644 index 00000000000..dfe27642322 --- /dev/null +++ b/drivers/net/wireless/libertas/decl.h @@ -0,0 +1,83 @@ +/** + * This file contains declaration referring to + * functions defined in other source files + */ + +#ifndef _WLAN_DECL_H_ +#define _WLAN_DECL_H_ + +#include "defs.h" + +/** Function Prototype Declaration */ +struct wlan_private; +struct sk_buff; +struct net_device; + +extern char *libertas_fw_name; + +void libertas_free_adapter(wlan_private * priv); +int libertas_set_mac_packet_filter(wlan_private * priv); + +int libertas_send_null_packet(wlan_private * priv, u8 pwr_mgmt); +void libertas_send_tx_feedback(wlan_private * priv); +u8 libertas_check_last_packet_indication(wlan_private * priv); + +int libertas_free_cmd_buffer(wlan_private * priv); +struct cmd_ctrl_node; +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv); + +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf); + +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf); + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail); + +int libertas_allocate_cmd_buffer(wlan_private * priv); +int libertas_execute_next_command(wlan_private * priv); +int libertas_process_event(wlan_private * priv); +void libertas_interrupt(struct net_device *); +int libertas_set_radio_control(wlan_private * priv); +u32 libertas_index_to_data_rate(u8 index); +u8 libertas_data_rate_to_index(u32 rate); +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen); + +void libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb); + +/** The proc fs interface */ +int libertas_process_rx_command(wlan_private * priv); +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb); +void libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band); + +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *); + +void libertas_ps_sleep(wlan_private * priv, int wait_option); +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode); +void libertas_ps_wakeup(wlan_private * priv, int wait_option); + +void libertas_tx_runqueue(wlan_private *priv); + +extern struct chan_freq_power *libertas_find_cfp_by_band_and_channel( + wlan_adapter * adapter, u8 band, u16 channel); + +extern void libertas_mac_event_disconnected(wlan_private * priv); + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str); + +int reset_device(wlan_private *priv); +/* main.c */ +extern struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, + int *cfp_no); +wlan_private *wlan_add_card(void *card); +int wlan_remove_card(void *card); + +#endif /* _WLAN_DECL_H_ */ diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h new file mode 100644 index 00000000000..80dd9ea19c8 --- /dev/null +++ b/drivers/net/wireless/libertas/defs.h @@ -0,0 +1,327 @@ +/** + * This header file contains global constant/enum definitions, + * global variable declaration. + */ +#ifndef _WLAN_DEFS_H_ +#define _WLAN_DEFS_H_ + +#include <linux/spinlock.h> + +extern unsigned int libertas_debug; + +#ifdef CONFIG_LIBERTAS_DEBUG +#define DEBUG +#define PROC_DEBUG +#endif + +#define DRV_NAME "usb8xxx" + +#define lbs_pr_info(format, args...) \ + printk(KERN_INFO DRV_NAME": " format, ## args) +#define lbs_pr_err(format, args...) \ + printk(KERN_ERR DRV_NAME": " format, ## args) +#define lbs_pr_alert(format, args...) \ + printk(KERN_ALERT DRV_NAME": " format, ## args) + +#ifdef DEBUG +#define lbs_pr_debug(level, format, args...) \ + do { if (libertas_debug >= level) \ + printk(KERN_INFO DRV_NAME": " format, ##args); } while (0) +#define lbs_dev_dbg(level, device, format, args...) \ + lbs_pr_debug(level, "%s: " format, \ + (device)->bus_id , ## args) + +static inline void lbs_dbg_hex(char *prompt, u8 * buf, int len) +{ + int i = 0; + + if (!libertas_debug) + return; + + printk(KERN_DEBUG "%s: ", prompt); + for (i = 1; i <= len; i++) { + printk(KERN_DEBUG "%02x ", (u8) * buf); + buf++; + } + printk("\n"); +} +#else +#define lbs_pr_debug(level, format, args...) do {} while (0) +#define lbs_dev_dbg(level, device, format, args...) do {} while (0) +#define lbs_dbg_hex(x,y,z) do {} while (0) +#endif + +#define ENTER() lbs_pr_debug(1, "Enter: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) +#define LEAVE() lbs_pr_debug(1, "Leave: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) + +/** Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical +* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas +* driver has more local TxPDs. Each TxPD on the host memory is associated +* with a Tx control node. The driver maintains 8 RxPD descriptors for +* station firmware to store Rx packet information. +* +* Current version of MAC has a 32x6 multicast address buffer. +* +* 802.11b can have up to 14 channels, the driver keeps the +* BSSID(MAC address) of each APs or Ad hoc stations it has sensed. +*/ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define MRVDRV_NUM_OF_CMD_BUFFER 10 +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_MAX_BSSID_LIST 64 +#define MRVDRV_ASSOCIATION_TIME_OUT 255 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define WLAN_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/** Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_BSS_DESCRIPTS 16 +#define MRVDRV_MAX_REGION_CODE 6 + +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +#define MRVDRV_MIN_MULTIPLE_DTIM 1 +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +#define MRVDRV_DEFAULT_MULTIPLE_DTIM 1 + +#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 + +#define MRVDRV_CHANNELS_PER_SCAN 4 +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +#define MRVDRV_DEBUG_RX_PATH 0x00000001 +#define MRVDRV_DEBUG_TX_PATH 0x00000002 + +#define MRVDRV_MIN_BEACON_INTERVAL 20 +#define MRVDRV_MAX_BEACON_INTERVAL 1000 +#define MRVDRV_BEACON_INTERVAL 100 + +/** TxPD status */ + +/* Station firmware use TxPD status field to report final Tx transmit +* result, Bit masks are used to present combined situations. +*/ + +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/** Tx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define TxPD_CONTROL_WDS_FRAME (1<<17) +#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME + +/** RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + +/** RxPD status - Received packet types */ +/** Rx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define RxPD_CONTROL_WDS_FRAME (0x40) +#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME + +/** RSSI-related defines */ +/* RSSI constants are used to implement 802.11 RSSI threshold +* indication. if the Rx packet signal got too weak for 5 consecutive +* times, miniport driver (driver) will report this event to wrapper +*/ + +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +/** RTS/FRAG related defines */ +#define MRVDRV_RTS_MIN_VALUE 0 +#define MRVDRV_RTS_MAX_VALUE 2347 +#define MRVDRV_FRAG_MIN_VALUE 256 +#define MRVDRV_FRAG_MAX_VALUE 2346 + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) + +/** WPA key LENGTH*/ +#define MRVL_MAX_KEY_WPA_KEY_LENGTH 32 + +#define KEY_LEN_WPA_AES 16 +#define KEY_LEN_WPA_TKIP 32 +#define KEY_LEN_WEP_104 13 +#define KEY_LEN_WEP_40 5 + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define BAND_B (0x01) +#define BAND_G (0x02) +#define ALL_802_11_BANDS (BAND_B | BAND_G) + +/** MACRO DEFINITIONS */ +#define CAL_NF(NF) ((s32)(-(s32)(NF))) +#define CAL_RSSI(SNR, NF) ((s32)((s32)(SNR) + CAL_NF(NF))) +#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) + +#define DEFAULT_BCN_AVG_FACTOR 8 +#define DEFAULT_DATA_AVG_FACTOR 8 +#define AVG_SCALE 100 +#define CAL_AVG_SNR_NF(AVG, SNRNF, N) \ + (((AVG) == 0) ? ((u16)(SNRNF) * AVG_SCALE) : \ + ((((int)(AVG) * (N -1)) + ((u16)(SNRNF) * \ + AVG_SCALE)) / N)) + +#define B_SUPPORTED_RATES 8 +#define G_SUPPORTED_RATES 14 + +#define WLAN_SUPPORTED_RATES 14 + +#define MAX_LEDS 8 + +#define IS_MESH_FRAME(x) (x->cb[6]) +#define SET_MESH_FRAME(x) (x->cb[6]=1) +#define UNSET_MESH_FRAME(x) (x->cb[6]=0) + +/** Global Variable Declaration */ +typedef struct _wlan_private wlan_private; +typedef struct _wlan_adapter wlan_adapter; +extern const char libertas_driver_version[]; +extern u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE]; + +extern u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES]; + +extern u8 libertas_supported_rates[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_b[4]; + +/** ENUM definition*/ +/** SNRNF_TYPE */ +enum SNRNF_TYPE { + TYPE_BEACON = 0, + TYPE_RXPD, + MAX_TYPE_B +}; + +/** SNRNF_DATA*/ +enum SNRNF_DATA { + TYPE_NOAVG = 0, + TYPE_AVG, + MAX_TYPE_AVG +}; + +/** WLAN_802_11_POWER_MODE */ +enum WLAN_802_11_POWER_MODE { + wlan802_11powermodecam, + wlan802_11powermodemax_psp, + wlan802_11Powermodefast_psp, + /*not a real mode, defined as an upper bound */ + wlan802_11powemodemax +}; + +/** PS_STATE */ +enum PS_STATE { + PS_STATE_FULL_POWER, + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP +}; + +/** DNLD_STATE */ +enum DNLD_STATE { + DNLD_RES_RECEIVED, + DNLD_DATA_SENT, + DNLD_CMD_SENT +}; + +/** WLAN_MEDIA_STATE */ +enum WLAN_MEDIA_STATE { + libertas_connected, + libertas_disconnected +}; + +/** WLAN_802_11_PRIVACY_FILTER */ +enum WLAN_802_11_PRIVACY_FILTER { + wlan802_11privfilteracceptall, + wlan802_11privfilter8021xWEP +}; + +/** mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +/** SNMP_MIB_INDEX_e */ +enum SNMP_MIB_INDEX_e { + desired_bsstype_i = 0, + op_rateset_i, + bcnperiod_i, + dtimperiod_i, + assocrsp_timeout_i, + rtsthresh_i, + short_retrylim_i, + long_retrylim_i, + fragthresh_i, + dot11d_i, + dot11h_i, + manufid_i, + prodID_i, + manuf_oui_i, + manuf_name_i, + manuf_prodname_i, + manuf_prodver_i, +}; + +/** KEY_TYPE_ID */ +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES +}; + +/** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ +enum KEY_INFO_WPA { + KEY_INFO_WPA_MCAST = 0x01, + KEY_INFO_WPA_UNICAST = 0x02, + KEY_INFO_WPA_ENABLED = 0x04 +}; + +/** SNMP_MIB_VALUE_e */ +enum SNMP_MIB_VALUE_e { + SNMP_MIB_VALUE_INFRA = 1, + SNMP_MIB_VALUE_ADHOC +}; + +/* Default values for fwt commands. */ +#define FWT_DEFAULT_METRIC 0 +#define FWT_DEFAULT_DIR 1 +#define FWT_DEFAULT_SSN 0xffffffff +#define FWT_DEFAULT_DSN 0 +#define FWT_DEFAULT_HOPCOUNT 0 +#define FWT_DEFAULT_TTL 0 +#define FWT_DEFAULT_EXPIRATION 0 +#define FWT_DEFAULT_SLEEPMODE 0 +#define FWT_DEFAULT_SNR 0 + +#endif /* _WLAN_DEFS_H_ */ diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h new file mode 100644 index 00000000000..e8b9020f9bd --- /dev/null +++ b/drivers/net/wireless/libertas/dev.h @@ -0,0 +1,401 @@ +/** + * This file contains definitions and data structures specific + * to Marvell 802.11 NIC. It contains the Device Information + * structure wlan_adapter. + */ +#ifndef _WLAN_DEV_H_ +#define _WLAN_DEV_H_ + +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <linux/ethtool.h> +#include <linux/debugfs.h> +#include <net/ieee80211.h> + +#include "defs.h" +#include "scan.h" +#include "thread.h" + +extern struct ethtool_ops libertas_ethtool_ops; + +#define MAX_BSSID_PER_CHANNEL 16 + +#define NR_TX_QUEUE 3 + +/* For the extended Scan */ +#define MAX_EXTENDED_SCAN_BSSID_LIST MAX_BSSID_PER_CHANNEL * \ + MRVDRV_MAX_CHANNEL_SIZE + 1 + +#define MAX_REGION_CHANNEL_NUM 2 + +/** Chan-freq-TxPower mapping table*/ +struct chan_freq_power { + /** channel Number */ + u16 channel; + /** frequency of this channel */ + u32 freq; + /** Max allowed Tx power level */ + u16 maxtxpower; + /** TRUE:channel unsupported; FLASE:supported*/ + u8 unsupported; +}; + +/** region-band mapping table*/ +struct region_channel { + /** TRUE if this entry is valid */ + u8 valid; + /** region code for US, Japan ... */ + u8 region; + /** band B/G/A, used for BAND_CONFIG cmd */ + u8 band; + /** Actual No. of elements in the array below */ + u8 nrcfp; + /** chan-freq-txpower mapping table*/ + struct chan_freq_power *CFP; +}; + +struct wlan_802_11_security { + u8 WPAenabled; + u8 WPA2enabled; + u8 wep_enabled; + u8 auth_mode; +}; + +/** Current Basic Service Set State Structure */ +struct current_bss_params { + struct bss_descriptor bssdescriptor; + /** bssid */ + u8 bssid[ETH_ALEN]; + /** ssid */ + struct WLAN_802_11_SSID ssid; + + /** band */ + u8 band; + /** channel */ + u8 channel; + /** number of rates supported */ + int numofrates; + /** supported rates*/ + u8 datarates[WLAN_SUPPORTED_RATES]; +}; + +/** sleep_params */ +struct sleep_params { + u16 sp_error; + u16 sp_offset; + u16 sp_stabletime; + u8 sp_calcontrol; + u8 sp_extsleepclk; + u16 sp_reserved; +}; + +/** Data structure for the Marvell WLAN device */ +typedef struct _wlan_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** Upload received */ + u32 upld_rcv; + /** Upload type */ + u32 upld_typ; + /** Upload length */ + u32 upld_len; + /** netdev pointer */ + struct net_device *netdev; + /* Upload buffer */ + u8 upld_buf[WLAN_UPLD_SIZE]; + /* Download sent: + bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + u8 dnld_sent; +} wlan_dev_t, *pwlan_dev_t; + +/* Mesh statistics */ +struct wlan_mesh_stats { + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ + u32 fwd_drop_ttl; /* Fwd: TTL zero */ + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ + u32 fwd_drop_noroute; /* Fwd: No route to Destination */ + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ + u32 drop_blind; /* Rx: Dropped by blinding table */ +}; + +/** Private structure for the MV device */ +struct _wlan_private { + int open; + int mesh_open; + int infra_open; + + wlan_adapter *adapter; + wlan_dev_t wlan_dev; + + struct net_device_stats stats; + struct net_device *mesh_dev ; /* Virtual device */ + + struct iw_statistics wstats; + struct wlan_mesh_stats mstats; + struct dentry *debugfs_dir; + struct dentry *debugfs_debug; + struct dentry *debugfs_files[6]; + + struct dentry *events_dir; + struct dentry *debugfs_events_files[6]; + + struct dentry *regs_dir; + struct dentry *debugfs_regs_files[6]; + + u32 mac_offset; + u32 bbp_offset; + u32 rf_offset; + + const struct firmware *firmware; + struct device *hotplug_device; + + /** thread to service interrupts */ + struct wlan_thread mainthread; + + struct delayed_work assoc_work; + struct workqueue_struct *assoc_thread; +}; + +/** Association request + * + * Encapsulates all the options that describe a specific assocation request + * or configuration of the wireless card's radio, mode, and security settings. + */ +struct assoc_request { +#define ASSOC_FLAG_SSID 1 +#define ASSOC_FLAG_CHANNEL 2 +#define ASSOC_FLAG_MODE 3 +#define ASSOC_FLAG_BSSID 4 +#define ASSOC_FLAG_WEP_KEYS 5 +#define ASSOC_FLAG_WEP_TX_KEYIDX 6 +#define ASSOC_FLAG_WPA_MCAST_KEY 7 +#define ASSOC_FLAG_WPA_UCAST_KEY 8 +#define ASSOC_FLAG_SECINFO 9 +#define ASSOC_FLAG_WPA_IE 10 + unsigned long flags; + + struct WLAN_802_11_SSID ssid; + u8 channel; + u8 mode; + u8 bssid[ETH_ALEN]; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + struct wlan_802_11_security secinfo; + + /** WPA Information Elements*/ + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; +}; + +/** Wlan adapter data structure*/ +struct _wlan_adapter { + /** STATUS variables */ + u32 fwreleasenumber; + u32 fwcapinfo; + /* protected with big lock */ + + struct mutex lock; + + u8 tmptxbuf[WLAN_UPLD_SIZE]; + /* protected by hard_start_xmit serialization */ + + /** command-related variables */ + u16 seqnum; + /* protected by big lock */ + + struct cmd_ctrl_node *cmd_array; + /** Current command */ + struct cmd_ctrl_node *cur_cmd; + int cur_cmd_retcode; + /** command Queues */ + /** Free command buffers */ + struct list_head cmdfreeq; + /** Pending command buffers */ + struct list_head cmdpendingq; + + wait_queue_head_t cmd_pending; + u8 nr_cmd_pending; + /* command related variables protected by adapter->driver_lock */ + + /** Async and Sync Event variables */ + u32 intcounter; + u32 eventcause; + u8 nodename[16]; /* nickname */ + + /** spin locks */ + spinlock_t driver_lock; + + /** Timers */ + struct timer_list command_timer; + + /* TX queue used in PS mode */ + spinlock_t txqueue_lock; + struct sk_buff *tx_queue_ps[NR_TX_QUEUE]; + unsigned int tx_queue_idx; + + u8 hisregcpy; + + /** current ssid/bssid related parameters*/ + struct current_bss_params curbssparams; + + /* IW_MODE_* */ + u8 mode; + + struct bss_descriptor *pattemptedbssdesc; + + struct WLAN_802_11_SSID previousssid; + u8 previousbssid[ETH_ALEN]; + + struct bss_descriptor *scantable; + u32 numinscantable; + + u8 scantype; + u32 scanmode; + + u16 beaconperiod; + u8 adhoccreate; + + /** capability Info used in Association, start, join */ + struct ieeetypes_capinfo capinfo; + + /** MAC address information */ + u8 current_addr[ETH_ALEN]; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + u32 nr_of_multicastmacaddr; + + /** 802.11 statistics */ +// struct cmd_DS_802_11_GET_STAT wlan802_11Stat; + + u16 enablehwauto; + u16 ratebitmap; + /** control G rates */ + u8 adhoc_grate_enabled; + + u32 txantenna; + u32 rxantenna; + + u8 adhocchannel; + u32 fragthsd; + u32 rtsthsd; + + u32 datarate; + u8 is_datarate_auto; + + u16 listeninterval; + u16 prescan; + u8 txretrycount; + + /** Tx-related variables (for single packet tx) */ + struct sk_buff *currenttxskb; + u16 TxLockFlag; + + /** NIC Operation characteristics */ + u16 currentpacketfilter; + u32 connect_status; + u16 regioncode; + u16 regiontableindex; + u16 txpowerlevel; + + /** POWER MANAGEMENT AND PnP SUPPORT */ + u8 surpriseremoved; + u16 atimwindow; + + u16 psmode; /* Wlan802_11PowermodeCAM=disable + Wlan802_11PowermodeMAX_PSP=enable */ + u16 multipledtim; + u32 psstate; + u8 needtowakeup; + + struct PS_CMD_ConfirmSleep libertas_ps_confirm_sleep; + u16 locallisteninterval; + u16 nullpktinterval; + + struct assoc_request * assoc_req; + + /** Encryption parameter */ + struct wlan_802_11_security secinfo; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + /** WPA Information Elements*/ + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; + + u16 rxantennamode; + u16 txantennamode; + + /** Requested Signal Strength*/ + u16 bcn_avg_factor; + u16 data_avg_factor; + u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG]; + u16 NF[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 RSSI[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 rawSNR[DEFAULT_DATA_AVG_FACTOR]; + u8 rawNF[DEFAULT_DATA_AVG_FACTOR]; + u16 nextSNRNF; + u16 numSNRNF; + u16 rxpd_rate; + + u8 radioon; + u32 preamble; + + /** Multi bands Parameter*/ + u8 libertas_supported_rates[G_SUPPORTED_RATES]; + + /** Blue Tooth Co-existence Arbitration */ + + /** sleep_params */ + struct sleep_params sp; + + /** RF calibration data */ + +#define MAX_REGION_CHANNEL_NUM 2 + /** region channel data */ + struct region_channel region_channel[MAX_REGION_CHANNEL_NUM]; + + struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM]; + + /** 11D and Domain Regulatory Data */ + struct wlan_802_11d_domain_reg domainreg; + struct parsed_region_chan_11d parsed_region_chan; + + /** FSM variable for 11d support */ + u32 enable11d; + + /** MISCELLANEOUS */ + u8 *prdeeprom; + struct wlan_offset_value offsetvalue; + + struct cmd_ds_802_11_get_log logmsg; + u16 scanprobes; + + u32 pkttxctrl; + + u16 txrate; + u32 linkmode; + u32 radiomode; + u32 debugmode; + u8 fw_ready; +}; + +#endif /* _WLAN_DEV_H_ */ diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c new file mode 100644 index 00000000000..0064de54296 --- /dev/null +++ b/drivers/net/wireless/libertas/ethtool.c @@ -0,0 +1,184 @@ + +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/delay.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" +static const char * mesh_stat_strings[]= { + "drop_duplicate_bcast", + "drop_ttl_zero", + "drop_no_fwd_route", + "drop_no_buffers", + "fwded_unicast_cnt", + "fwded_bcast_cnt", + "drop_blind_table" +}; + +static void libertas_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + wlan_private *priv = (wlan_private *) dev->priv; + char fwver[32]; + + libertas_get_fwversion(priv->adapter, fwver, sizeof(fwver) - 1); + + strcpy(info->driver, "libertas"); + strcpy(info->version, libertas_driver_version); + strcpy(info->fw_version, fwver); +} + +/* All 8388 parts have 16KiB EEPROM size at the time of writing. + * In case that changes this needs fixing. + */ +#define LIBERTAS_EEPROM_LEN 16384 + +static int libertas_ethtool_get_eeprom_len(struct net_device *dev) +{ + return LIBERTAS_EEPROM_LEN; +} + +static int libertas_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr regctrl; + char *ptr; + int ret; + + regctrl.action = 0; + regctrl.offset = eeprom->offset; + regctrl.NOB = eeprom->len; + + if (eeprom->offset + eeprom->len > LIBERTAS_EEPROM_LEN) + return -EINVAL; + +// mutex_lock(&priv->mutex); + + adapter->prdeeprom = + (char *)kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL); + if (!adapter->prdeeprom) + return -ENOMEM; + memcpy(adapter->prdeeprom, ®ctrl, sizeof(regctrl)); + + /* +14 is for action, offset, and NOB in + * response */ + lbs_pr_debug(1, "action:%d offset: %x NOB: %02x\n", + regctrl.action, regctrl.offset, regctrl.NOB); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_eeprom_access, + regctrl.action, + cmd_option_waitforrsp, 0, + ®ctrl); + + if (ret) { + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); + LEAVE(); + return ret; + } + + mdelay(10); + + ptr = (char *)adapter->prdeeprom; + + /* skip the command header, but include the "value" u32 variable */ + ptr = ptr + sizeof(struct wlan_ioctl_regrdwr) - 4; + + /* + * Return the result back to the user + */ + memcpy(bytes, ptr, eeprom->len); + + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); +// mutex_unlock(&priv->mutex); + + return 0; +} + +static void libertas_ethtool_get_stats(struct net_device * dev, + struct ethtool_stats * stats, u64 * data) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + stats->cmd = ETHTOOL_GSTATS; + BUG_ON(stats->n_stats != MESH_STATS_NUM); + + data[0] = priv->mstats.fwd_drop_rbt; + data[1] = priv->mstats.fwd_drop_ttl; + data[2] = priv->mstats.fwd_drop_noroute; + data[3] = priv->mstats.fwd_drop_nobuf; + data[4] = priv->mstats.fwd_unicast_cnt; + data[5] = priv->mstats.fwd_bcast_cnt; + data[6] = priv->mstats.drop_blind; + + LEAVE(); +} + +static int libertas_ethtool_get_stats_count(struct net_device * dev) +{ + int ret; + wlan_private *priv = dev->priv; + struct cmd_ds_mesh_access mesh_access; + + ENTER(); + /* Get Mesh Statistics */ + ret = libertas_prepare_and_send_command(priv, + cmd_mesh_access, cmd_act_mesh_get_stats, + cmd_option_waitforrsp, 0, &mesh_access); + + if (ret) { + LEAVE(); + return 0; + } + + priv->mstats.fwd_drop_rbt = mesh_access.data[0]; + priv->mstats.fwd_drop_ttl = mesh_access.data[1]; + priv->mstats.fwd_drop_noroute = mesh_access.data[2]; + priv->mstats.fwd_drop_nobuf = mesh_access.data[3]; + priv->mstats.fwd_unicast_cnt = mesh_access.data[4]; + priv->mstats.fwd_bcast_cnt = mesh_access.data[5]; + priv->mstats.drop_blind = mesh_access.data[6]; + + LEAVE(); + return MESH_STATS_NUM; +} + +static void libertas_ethtool_get_strings (struct net_device * dev, + u32 stringset, + u8 * s) +{ + int i; + + ENTER(); + switch (stringset) { + case ETH_SS_STATS: + for (i=0; i < MESH_STATS_NUM; i++) { + memcpy(s + i * ETH_GSTRING_LEN, + mesh_stat_strings[i], + ETH_GSTRING_LEN); + } + break; + } + LEAVE(); +} + +struct ethtool_ops libertas_ethtool_ops = { + .get_drvinfo = libertas_ethtool_get_drvinfo, + .get_eeprom = libertas_ethtool_get_eeprom, + .get_eeprom_len = libertas_ethtool_get_eeprom_len, + .get_stats_count = libertas_ethtool_get_stats_count, + .get_ethtool_stats = libertas_ethtool_get_stats, + .get_strings = libertas_ethtool_get_strings, +}; + diff --git a/drivers/net/wireless/libertas/fw.c b/drivers/net/wireless/libertas/fw.c new file mode 100644 index 00000000000..5c63c9b1659 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.c @@ -0,0 +1,362 @@ +/** + * This file contains the initialization for FW and HW + */ +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/vmalloc.h> +#include <linux/firmware.h> +#include <linux/version.h> + +#include "host.h" +#include "sbi.h" +#include "defs.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "if_usb.h" + +char *libertas_fw_name = NULL; +module_param_named(fw_name, libertas_fw_name, charp, 0644); + +unsigned int libertas_debug = 0; +module_param(libertas_debug, int, 0); + +/** + * @brief This function checks the validity of Boot2/FW image. + * + * @param data pointer to image + * len image length + * @return 0 or -1 + */ +static int check_fwfile_format(u8 *data, u32 totlen) +{ + u8 bincmd, exit; + u32 blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + bincmd = *data; + blksize = *(u32*)(data + offsetof(struct fwheader, datalength)); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + lbs_pr_err("bin file format check FAIL...\n"); + else + lbs_pr_debug(1, "bin file format check PASS...\n"); + + return ret; +} + +/** + * @brief This function downloads firmware image, gets + * HW spec from firmware and set basic parameters to + * firmware. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +static int wlan_setup_station_hw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if ((ret = request_firmware(&priv->firmware, libertas_fw_name, + priv->hotplug_device)) < 0) { + lbs_pr_err("request_firmware() failed, error code = %#x\n", + ret); + lbs_pr_err("%s not found in /lib/firmware\n", libertas_fw_name); + goto done; + } + + if(check_fwfile_format(priv->firmware->data, priv->firmware->size)) { + release_firmware(priv->firmware); + goto done; + } + + ret = libertas_sbi_prog_firmware(priv); + + release_firmware(priv->firmware); + + if (ret) { + lbs_pr_debug(1, "Bootloader in invalid state!\n"); + ret = -1; + goto done; + } + + /* + * Read MAC address from HW + */ + memset(adapter->current_addr, 0xff, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_get_hw_spec, + 0, cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + libertas_set_mac_packet_filter(priv); + + /* Get the supported Data rates */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + cmd_act_get_tx_rate, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + ret = 0; +done: + LEAVE(); + + return (ret); +} + +static int wlan_allocate_adapter(wlan_private * priv) +{ + u32 ulbufsize; + wlan_adapter *adapter = priv->adapter; + + struct bss_descriptor *ptempscantable; + + /* Allocate buffer to store the BSSID list */ + ulbufsize = sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST; + if (!(ptempscantable = kmalloc(ulbufsize, GFP_KERNEL))) { + libertas_free_adapter(priv); + return -1; + } + + adapter->scantable = ptempscantable; + memset(adapter->scantable, 0, ulbufsize); + + /* Allocate the command buffers */ + libertas_allocate_cmd_buffer(priv); + + memset(&adapter->libertas_ps_confirm_sleep, 0, sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.seqnum = cpu_to_le16(++adapter->seqnum); + adapter->libertas_ps_confirm_sleep.command = + cpu_to_le16(cmd_802_11_ps_mode); + adapter->libertas_ps_confirm_sleep.size = + cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.result = 0; + adapter->libertas_ps_confirm_sleep.action = + cpu_to_le16(cmd_subcmd_sleep_confirmed); + + return 0; +} + +static void wlan_init_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + adapter->scanprobes = 0; + + adapter->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + adapter->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + /* ATIM params */ + adapter->atimwindow = 0; + + adapter->connect_status = libertas_disconnected; + memset(adapter->current_addr, 0xff, ETH_ALEN); + + /* scan type */ + adapter->scantype = cmd_scan_type_active; + + /* scan mode */ + adapter->scanmode = cmd_bss_type_any; + + /* 802.11 specific */ + adapter->secinfo.wep_enabled = 0; + for (i = 0; i < sizeof(adapter->wep_keys) / sizeof(adapter->wep_keys[0]); + i++) + memset(&adapter->wep_keys[i], 0, sizeof(struct WLAN_802_11_KEY)); + adapter->wep_tx_keyidx = 0; + adapter->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + adapter->mode = IW_MODE_INFRA; + + adapter->assoc_req = NULL; + + adapter->numinscantable = 0; + adapter->pattemptedbssdesc = NULL; + mutex_init(&adapter->lock); + + adapter->prescan = 1; + + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + /* PnP and power profile */ + adapter->surpriseremoved = 0; + + adapter->currentpacketfilter = + cmd_act_mac_rx_on | cmd_act_mac_tx_on; + + adapter->radioon = RADIO_ON; + adapter->txantenna = RF_ANTENNA_2; + adapter->rxantenna = RF_ANTENNA_AUTO; + + adapter->is_datarate_auto = 1; + adapter->beaconperiod = MRVDRV_BEACON_INTERVAL; + + // set default value of capinfo. +#define SHORT_PREAMBLE_ALLOWED 1 + memset(&adapter->capinfo, 0, sizeof(adapter->capinfo)); + adapter->capinfo.shortpreamble = SHORT_PREAMBLE_ALLOWED; + + adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; + + adapter->psmode = wlan802_11powermodecam; + adapter->multipledtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; + + adapter->listeninterval = MRVDRV_DEFAULT_LISTEN_INTERVAL; + + adapter->psstate = PS_STATE_FULL_POWER; + adapter->needtowakeup = 0; + adapter->locallisteninterval = 0; /* default value in firmware will be used */ + + adapter->datarate = 0; // Initially indicate the rate as auto + + adapter->adhoc_grate_enabled = 0; + + adapter->intcounter = 0; + + adapter->currenttxskb = NULL; + adapter->pkttxctrl = 0; + + memset(&adapter->tx_queue_ps, 0, NR_TX_QUEUE*sizeof(struct sk_buff*)); + adapter->tx_queue_idx = 0; + spin_lock_init(&adapter->txqueue_lock); + + return; +} + +static void command_timer_fn(unsigned long data); + +int libertas_init_fw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate adapter structure */ + if ((ret = wlan_allocate_adapter(priv)) != 0) + goto done; + + /* init adapter structure */ + wlan_init_adapter(priv); + + /* init timer etc. */ + setup_timer(&adapter->command_timer, command_timer_fn, + (unsigned long)priv); + + /* download fimrware etc. */ + if ((ret = wlan_setup_station_hw(priv)) != 0) { + del_timer_sync(&adapter->command_timer); + goto done; + } + + /* init 802.11d */ + libertas_init_11d(priv); + + ret = 0; +done: + LEAVE(); + return ret; +} + +void libertas_free_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + + if (!adapter) { + lbs_pr_debug(1, "Why double free adapter?:)\n"); + return; + } + + lbs_pr_debug(1, "Free command buffer\n"); + libertas_free_cmd_buffer(priv); + + lbs_pr_debug(1, "Free commandTimer\n"); + del_timer(&adapter->command_timer); + + lbs_pr_debug(1, "Free scantable\n"); + if (adapter->scantable) { + kfree(adapter->scantable); + adapter->scantable = NULL; + } + + lbs_pr_debug(1, "Free adapter\n"); + + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; +} + +/** + * This function handles the timeout of command sending. + * It will re-send the same command again. + */ +static void command_timer_fn(unsigned long data) +{ + wlan_private *priv = (wlan_private *)data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *ptempnode; + struct cmd_ds_command *cmd; + unsigned long flags; + + ptempnode = adapter->cur_cmd; + if (ptempnode == NULL) { + lbs_pr_debug(1, "PTempnode Empty\n"); + return; + } + + cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr; + if (!cmd) { + lbs_pr_debug(1, "cmd is NULL\n"); + return; + } + + lbs_pr_info("command_timer_fn fired (%x)\n", cmd->command); + + if (!adapter->fw_ready) + return; + + spin_lock_irqsave(&adapter->driver_lock, flags); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "Re-sending same command as it timeout...!\n"); + libertas_queue_cmd(adapter, ptempnode, 0); + + wake_up_interruptible(&priv->mainthread.waitq); + + return; +} diff --git a/drivers/net/wireless/libertas/fw.h b/drivers/net/wireless/libertas/fw.h new file mode 100644 index 00000000000..1f9ae267a9e --- /dev/null +++ b/drivers/net/wireless/libertas/fw.h @@ -0,0 +1,13 @@ +/** + * This header file contains FW interface related definitions. + */ +#ifndef _WLAN_FW_H_ +#define _WLAN_FW_H_ + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +int libertas_init_fw(wlan_private * priv); + +#endif /* _WLAN_FW_H_ */ diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h new file mode 100644 index 00000000000..c0faaecaf5b --- /dev/null +++ b/drivers/net/wireless/libertas/host.h @@ -0,0 +1,338 @@ +/** + * This file contains definitions of WLAN commands. + */ + +#ifndef _HOST_H_ +#define _HOST_H_ + +/** PUBLIC DEFINITIONS */ +#define DEFAULT_AD_HOC_CHANNEL 6 +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +/** IEEE 802.11 oids */ +#define OID_802_11_SSID 0x00008002 +#define OID_802_11_INFRASTRUCTURE_MODE 0x00008008 +#define OID_802_11_FRAGMENTATION_THRESHOLD 0x00008009 +#define OID_802_11_RTS_THRESHOLD 0x0000800A +#define OID_802_11_TX_ANTENNA_SELECTED 0x0000800D +#define OID_802_11_SUPPORTED_RATES 0x0000800E +#define OID_802_11_STATISTICS 0x00008012 +#define OID_802_11_TX_RETRYCOUNT 0x0000801D +#define OID_802_11D_ENABLE 0x00008020 + +#define cmd_option_waitforrsp 0x0002 + +/** Host command ID */ +#define cmd_code_dnld 0x0002 +#define cmd_get_hw_spec 0x0003 +#define cmd_eeprom_update 0x0004 +#define cmd_802_11_reset 0x0005 +#define cmd_802_11_scan 0x0006 +#define cmd_802_11_get_log 0x000b +#define cmd_mac_multicast_adr 0x0010 +#define cmd_802_11_authenticate 0x0011 +#define cmd_802_11_eeprom_access 0x0059 +#define cmd_802_11_associate 0x0050 +#define cmd_802_11_set_wep 0x0013 +#define cmd_802_11_get_stat 0x0014 +#define cmd_802_3_get_stat 0x0015 +#define cmd_802_11_snmp_mib 0x0016 +#define cmd_mac_reg_map 0x0017 +#define cmd_bbp_reg_map 0x0018 +#define cmd_mac_reg_access 0x0019 +#define cmd_bbp_reg_access 0x001a +#define cmd_rf_reg_access 0x001b +#define cmd_802_11_radio_control 0x001c +#define cmd_802_11_rf_channel 0x001d +#define cmd_802_11_rf_tx_power 0x001e +#define cmd_802_11_rssi 0x001f +#define cmd_802_11_rf_antenna 0x0020 + +#define cmd_802_11_ps_mode 0x0021 + +#define cmd_802_11_data_rate 0x0022 +#define cmd_rf_reg_map 0x0023 +#define cmd_802_11_deauthenticate 0x0024 +#define cmd_802_11_reassociate 0x0025 +#define cmd_802_11_disassociate 0x0026 +#define cmd_mac_control 0x0028 +#define cmd_802_11_ad_hoc_start 0x002b +#define cmd_802_11_ad_hoc_join 0x002c + +#define cmd_802_11_query_tkip_reply_cntrs 0x002e +#define cmd_802_11_enable_rsn 0x002f +#define cmd_802_11_pairwise_tsc 0x0036 +#define cmd_802_11_group_tsc 0x0037 +#define cmd_802_11_key_material 0x005e + +#define cmd_802_11_set_afc 0x003c +#define cmd_802_11_get_afc 0x003d + +#define cmd_802_11_ad_hoc_stop 0x0040 + +#define cmd_802_11_beacon_stop 0x0049 + +#define cmd_802_11_mac_address 0x004D +#define cmd_802_11_eeprom_access 0x0059 + +#define cmd_802_11_band_config 0x0058 + +#define cmd_802_11d_domain_info 0x005b + +#define cmd_802_11_sleep_params 0x0066 + +#define cmd_802_11_inactivity_timeout 0x0067 + +#define cmd_802_11_tpc_cfg 0x0072 +#define cmd_802_11_pwr_cfg 0x0073 + +#define cmd_802_11_led_gpio_ctrl 0x004e + +#define cmd_802_11_subscribe_event 0x0075 + +#define cmd_802_11_rate_adapt_rateset 0x0076 + +#define cmd_802_11_tx_rate_query 0x007f + +#define cmd_get_tsf 0x0080 + +#define cmd_bt_access 0x0087 +#define cmd_ret_bt_access 0x8087 + +#define cmd_fwt_access 0x0088 +#define cmd_ret_fwt_access 0x8088 + +#define cmd_mesh_access 0x0090 +#define cmd_ret_mesh_access 0x8090 + +/* For the IEEE Power Save */ +#define cmd_subcmd_enter_ps 0x0030 +#define cmd_subcmd_exit_ps 0x0031 +#define cmd_subcmd_sleep_confirmed 0x0034 +#define cmd_subcmd_full_powerdown 0x0035 +#define cmd_subcmd_full_powerup 0x0036 + +/* command RET code, MSB is set to 1 */ +#define cmd_ret_hw_spec_info 0x8003 +#define cmd_ret_eeprom_update 0x8004 +#define cmd_ret_802_11_reset 0x8005 +#define cmd_ret_802_11_scan 0x8006 +#define cmd_ret_802_11_get_log 0x800b +#define cmd_ret_mac_control 0x8028 +#define cmd_ret_mac_multicast_adr 0x8010 +#define cmd_ret_802_11_authenticate 0x8011 +#define cmd_ret_802_11_deauthenticate 0x8024 +#define cmd_ret_802_11_associate 0x8012 +#define cmd_ret_802_11_reassociate 0x8025 +#define cmd_ret_802_11_disassociate 0x8026 +#define cmd_ret_802_11_set_wep 0x8013 +#define cmd_ret_802_11_stat 0x8014 +#define cmd_ret_802_3_stat 0x8015 +#define cmd_ret_802_11_snmp_mib 0x8016 +#define cmd_ret_mac_reg_map 0x8017 +#define cmd_ret_bbp_reg_map 0x8018 +#define cmd_ret_rf_reg_map 0x8023 +#define cmd_ret_mac_reg_access 0x8019 +#define cmd_ret_bbp_reg_access 0x801a +#define cmd_ret_rf_reg_access 0x801b +#define cmd_ret_802_11_radio_control 0x801c +#define cmd_ret_802_11_rf_channel 0x801d +#define cmd_ret_802_11_rssi 0x801f +#define cmd_ret_802_11_rf_tx_power 0x801e +#define cmd_ret_802_11_rf_antenna 0x8020 +#define cmd_ret_802_11_ps_mode 0x8021 +#define cmd_ret_802_11_data_rate 0x8022 + +#define cmd_ret_802_11_ad_hoc_start 0x802B +#define cmd_ret_802_11_ad_hoc_join 0x802C + +#define cmd_ret_802_11_query_tkip_reply_cntrs 0x802e +#define cmd_ret_802_11_enable_rsn 0x802f +#define cmd_ret_802_11_pairwise_tsc 0x8036 +#define cmd_ret_802_11_group_tsc 0x8037 +#define cmd_ret_802_11_key_material 0x805e + +#define cmd_enable_rsn 0x0001 +#define cmd_disable_rsn 0x0000 + +#define cmd_act_set 0x0001 +#define cmd_act_get 0x0000 + +#define cmd_act_get_AES (cmd_act_get + 2) +#define cmd_act_set_AES (cmd_act_set + 2) +#define cmd_act_remove_aes (cmd_act_set + 3) + +#define cmd_ret_802_11_set_afc 0x803c +#define cmd_ret_802_11_get_afc 0x803d + +#define cmd_ret_802_11_ad_hoc_stop 0x8040 + +#define cmd_ret_802_11_beacon_stop 0x8049 + +#define cmd_ret_802_11_mac_address 0x804D +#define cmd_ret_802_11_eeprom_access 0x8059 + +#define cmd_ret_802_11_band_config 0x8058 + +#define cmd_ret_802_11_sleep_params 0x8066 + +#define cmd_ret_802_11_inactivity_timeout 0x8067 + +#define cmd_ret_802_11d_domain_info (0x8000 | \ + cmd_802_11d_domain_info) + +#define cmd_ret_802_11_tpc_cfg (cmd_802_11_tpc_cfg | 0x8000) +#define cmd_ret_802_11_pwr_cfg (cmd_802_11_pwr_cfg | 0x8000) + +#define cmd_ret_802_11_led_gpio_ctrl 0x804e + +#define cmd_ret_802_11_subscribe_event (cmd_802_11_subscribe_event | 0x8000) + +#define cmd_ret_802_11_rate_adapt_rateset (cmd_802_11_rate_adapt_rateset | 0x8000) + +#define cmd_rte_802_11_tx_rate_query (cmd_802_11_tx_rate_query | 0x8000) + +#define cmd_ret_get_tsf 0x8080 + +/* Define action or option for cmd_802_11_set_wep */ +#define cmd_act_add 0x0002 +#define cmd_act_remove 0x0004 +#define cmd_act_use_default 0x0008 + +#define cmd_type_wep_40_bit 0x0001 +#define cmd_type_wep_104_bit 0x0002 + +#define cmd_NUM_OF_WEP_KEYS 4 + +#define cmd_WEP_KEY_INDEX_MASK 0x3fff + +/* Define action or option for cmd_802_11_reset */ +#define cmd_act_halt 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_bss_type_bss 0x0001 +#define cmd_bss_type_ibss 0x0002 +#define cmd_bss_type_any 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_scan_type_active 0x0000 +#define cmd_scan_type_passive 0x0001 + +#define cmd_scan_radio_type_bg 0 + +#define cmd_scan_probe_delay_time 0 + +/* Define action or option for cmd_mac_control */ +#define cmd_act_mac_rx_on 0x0001 +#define cmd_act_mac_tx_on 0x0002 +#define cmd_act_mac_loopback_on 0x0004 +#define cmd_act_mac_wep_enable 0x0008 +#define cmd_act_mac_int_enable 0x0010 +#define cmd_act_mac_multicast_enable 0x0020 +#define cmd_act_mac_broadcast_enable 0x0040 +#define cmd_act_mac_promiscuous_enable 0x0080 +#define cmd_act_mac_all_multicast_enable 0x0100 +#define cmd_act_mac_strict_protection_enable 0x0400 + +/* Define action or option for cmd_802_11_radio_control */ +#define cmd_type_auto_preamble 0x0001 +#define cmd_type_short_preamble 0x0002 +#define cmd_type_long_preamble 0x0003 + +#define TURN_ON_RF 0x01 +#define RADIO_ON 0x01 +#define RADIO_OFF 0x00 + +#define SET_AUTO_PREAMBLE 0x05 +#define SET_SHORT_PREAMBLE 0x03 +#define SET_LONG_PREAMBLE 0x01 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define cmd_opt_802_11_rf_channel_get 0x00 +#define cmd_opt_802_11_rf_channel_set 0x01 + +/* Define action or option for cmd_802_11_rf_tx_power */ +#define cmd_act_tx_power_opt_get 0x0000 +#define cmd_act_tx_power_opt_set_high 0x8007 +#define cmd_act_tx_power_opt_set_mid 0x8004 +#define cmd_act_tx_power_opt_set_low 0x8000 + +#define cmd_act_tx_power_index_high 0x0007 +#define cmd_act_tx_power_index_mid 0x0004 +#define cmd_act_tx_power_index_low 0x0000 + +/* Define action or option for cmd_802_11_data_rate */ +#define cmd_act_set_tx_auto 0x0000 +#define cmd_act_set_tx_fix_rate 0x0001 +#define cmd_act_get_tx_rate 0x0002 + +#define cmd_act_set_rx 0x0001 +#define cmd_act_set_tx 0x0002 +#define cmd_act_set_both 0x0003 +#define cmd_act_get_rx 0x0004 +#define cmd_act_get_tx 0x0008 +#define cmd_act_get_both 0x000c + +/* Define action or option for cmd_802_11_ps_mode */ +#define cmd_type_cam 0x0000 +#define cmd_type_max_psp 0x0001 +#define cmd_type_fast_psp 0x0002 + +/* Define action or option for cmd_bt_access */ +enum cmd_bt_access_opts { + /* The bt commands start at 5 instead of 1 because the old dft commands + * are mapped to 1-4. These old commands are no longer maintained and + * should not be called. + */ + cmd_act_bt_access_add = 5, + cmd_act_bt_access_del, + cmd_act_bt_access_list, + cmd_act_bt_access_reset +}; + +/* Define action or option for cmd_fwt_access */ +enum cmd_fwt_access_opts { + cmd_act_fwt_access_add = 1, + cmd_act_fwt_access_del, + cmd_act_fwt_access_lookup, + cmd_act_fwt_access_list, + cmd_act_fwt_access_list_route, + cmd_act_fwt_access_list_neighbor, + cmd_act_fwt_access_reset, + cmd_act_fwt_access_cleanup, + cmd_act_fwt_access_time, +}; + +/* Define action or option for cmd_mesh_access */ +enum cmd_mesh_access_opts { + cmd_act_mesh_get_ttl = 1, + cmd_act_mesh_set_ttl, + cmd_act_mesh_get_stats, + cmd_act_mesh_get_mpp, + cmd_act_mesh_set_mpp, +}; + +/** Card Event definition */ +#define MACREG_INT_CODE_TX_PPA_FREE 0x00000000 +#define MACREG_INT_CODE_TX_DMA_DONE 0x00000001 +#define MACREG_INT_CODE_LINK_LOSE_W_SCAN 0x00000002 +#define MACREG_INT_CODE_LINK_LOSE_NO_SCAN 0x00000003 +#define MACREG_INT_CODE_LINK_SENSED 0x00000004 +#define MACREG_INT_CODE_CMD_FINISHED 0x00000005 +#define MACREG_INT_CODE_MIB_CHANGED 0x00000006 +#define MACREG_INT_CODE_INIT_DONE 0x00000007 +#define MACREG_INT_CODE_DEAUTHENTICATED 0x00000008 +#define MACREG_INT_CODE_DISASSOCIATED 0x00000009 +#define MACREG_INT_CODE_PS_AWAKE 0x0000000a +#define MACREG_INT_CODE_PS_SLEEP 0x0000000b +#define MACREG_INT_CODE_MIC_ERR_MULTICAST 0x0000000d +#define MACREG_INT_CODE_MIC_ERR_UNICAST 0x0000000e +#define MACREG_INT_CODE_WM_AWAKE 0x0000000f +#define MACREG_INT_CODE_ADHOC_BCN_LOST 0x00000011 +#define MACREG_INT_CODE_RSSI_LOW 0x00000019 +#define MACREG_INT_CODE_SNR_LOW 0x0000001a +#define MACREG_INT_CODE_MAX_FAIL 0x0000001b +#define MACREG_INT_CODE_RSSI_HIGH 0x0000001c +#define MACREG_INT_CODE_SNR_HIGH 0x0000001d + +#endif /* _HOST_H_ */ diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h new file mode 100644 index 00000000000..f239e5d2435 --- /dev/null +++ b/drivers/net/wireless/libertas/hostcmd.h @@ -0,0 +1,693 @@ +/* + * This file contains the function prototypes, data structure + * and defines for all the host/station commands + */ +#ifndef __HOSTCMD__H +#define __HOSTCMD__H + +#include <linux/wireless.h> +#include "11d.h" +#include "types.h" + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* Current Tx packet status */ + u32 tx_status; + /* Tx control */ + u32 tx_control; + u32 tx_packet_location; + /* Tx packet length */ + u16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Amount of time the packet has been queued in the driver (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +}; + +/* RxPD Descriptor */ +struct rxpd { + /* Current Rx packet status */ + u16 status; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + u16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + u32 pkt_ptr; + + /* Next Rx RxPD addr */ + u32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +}; + +struct cmd_ctrl_node { + /* CMD link list */ + struct list_head list; + u32 status; + /* CMD ID */ + u32 cmd_oid; + /*CMD wait option: wait for finish or no wait */ + u16 wait_option; + /* command parameter */ + void *pdata_buf; + /*command data */ + u8 *bufvirtualaddr; + u16 cmdflags; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + +/* WLAN_802_11_KEY + * + * Generic structure to hold all key types. key type (WEP40, WEP104, TKIP, AES) + * is determined from the keylength field. + */ +struct WLAN_802_11_KEY { + u32 len; + u32 flags; /* KEY_INFO_* from wlan_defs.h */ + u8 key[MRVL_MAX_KEY_WPA_KEY_LENGTH]; + u16 type; /* KEY_TYPE_* from wlan_defs.h */ +}; + +struct IE_WPA { + u8 elementid; + u8 len; + u8 oui[4]; + u16 version; +}; + +struct WLAN_802_11_SSID { + /* SSID length */ + u32 ssidlength; + + /* SSID information field */ + u8 ssid[IW_ESSID_MAX_SIZE]; +}; + +struct WPA_SUPPLICANT { + u8 wpa_ie[256]; + u8 wpa_ie_len; +}; + +/* wlan_offset_value */ +struct wlan_offset_value { + u32 offset; + u32 value; +}; + +struct WLAN_802_11_FIXED_IEs { + u8 timestamp[8]; + u16 beaconinterval; + u16 capabilities; +}; + +struct WLAN_802_11_VARIABLE_IEs { + u8 elementid; + u8 length; + u8 data[1]; +}; + +/* Define general data structure */ +/* cmd_DS_GEN */ +struct cmd_ds_gen { + u16 command; + u16 size; + u16 seqnum; + u16 result; +}; + +#define S_DS_GEN sizeof(struct cmd_ds_gen) +/* + * Define data structure for cmd_get_hw_spec + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + /* HW Interface version number */ + u16 hwifversion; + /* HW version number */ + u16 version; + /* Max number of TxPD FW can handle */ + u16 nr_txpd; + /* Max no of Multicast address */ + u16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + u16 regioncode; + + /* Number of antenna used */ + u16 nr_antenna; + + /* FW release number, example 0x1234=1.2.3.4 */ + u32 fwreleasenumber; + + /* Base Address of TxPD queue */ + u32 wcb_base; + /* Read Pointer of RxPd queue */ + u32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + u32 rxpd_wrptr; + + /*FW/HW capability */ + u32 fwcapinfo; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_reset { + u16 action; +}; + +struct cmd_ds_802_11_subscribe_event { + u16 action; + u16 events; +}; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for cmd_802_11_scan + */ +struct cmd_ds_802_11_scan { + u8 bsstype; + u8 BSSID[ETH_ALEN]; + u8 tlvbuffer[1]; +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_chanlistparamset_t ChanListParamSet; + mrvlietypes_ratesparamset_t OpRateSet; +#endif +}; + +struct cmd_ds_802_11_scan_rsp { + u16 bssdescriptsize; + u8 nr_sets; + u8 bssdesc_and_tlvbuffer[1]; +}; + +struct cmd_ds_802_11_get_log { + u32 mcasttxframe; + u32 failed; + u32 retry; + u32 multiretry; + u32 framedup; + u32 rtssuccess; + u32 rtsfailure; + u32 ackfailure; + u32 rxfrag; + u32 mcastrxframe; + u32 fcserror; + u32 txframe; + u32 wepundecryptable; +}; + +struct cmd_ds_mac_control { + u16 action; + u16 reserved; +}; + +struct cmd_ds_mac_multicast_adr { + u16 action; + u16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +}; + +struct cmd_ds_802_11_authenticate { + u8 macaddr[ETH_ALEN]; + u8 authtype; + u8 reserved[10]; +}; + +struct cmd_ds_802_11_deauthenticate { + u8 macaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate { + u8 peerstaaddr[6]; + struct ieeetypes_capinfo capinfo; + u16 listeninterval; + u16 bcnperiod; + u8 dtimperiod; + +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_phyparamset_t phyparamset; + mrvlietypes_ssparamset_t ssparamset; + mrvlietypes_ratesparamset_t ratesParamSet; +#endif +} __attribute__ ((packed)); + +struct cmd_ds_802_11_disassociate { + u8 destmacaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate_rsp { + struct ieeetypes_assocrsp assocRsp; +}; + +struct cmd_ds_802_11_ad_hoc_result { + u8 PAD[3]; + u8 BSSID[ETH_ALEN]; +}; + +struct cmd_ds_802_11_set_wep { + /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ + u16 action; + + /* key Index selected for Tx */ + u16 keyindex; + + /* 40, 128bit or TXWEP */ + u8 keytype[4]; + u8 keymaterial[4][16]; +}; + +struct cmd_ds_802_3_get_stat { + u32 xmitok; + u32 rcvok; + u32 xmiterror; + u32 rcverror; + u32 rcvnobuffer; + u32 rcvcrcerror; +}; + +struct cmd_ds_802_11_get_stat { + u32 txfragmentcnt; + u32 mcasttxframecnt; + u32 failedcnt; + u32 retrycnt; + u32 Multipleretrycnt; + u32 rtssuccesscnt; + u32 rtsfailurecnt; + u32 ackfailurecnt; + u32 frameduplicatecnt; + u32 rxfragmentcnt; + u32 mcastrxframecnt; + u32 fcserrorcnt; + u32 bcasttxframecnt; + u32 bcastrxframecnt; + u32 txbeacon; + u32 rxbeacon; + u32 wepundecryptable; +}; + +struct cmd_ds_802_11_snmp_mib { + u16 querytype; + u16 oid; + u16 bufsize; + u8 value[128]; +}; + +struct cmd_ds_mac_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_bbp_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_rf_reg_map { + u16 buffersize; + u8 regmap[64]; + u16 reserved; +}; + +struct cmd_ds_mac_reg_access { + u16 action; + u16 offset; + u32 value; +}; + +struct cmd_ds_bbp_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_rf_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_802_11_radio_control { + u16 action; + u16 control; +}; + +struct cmd_ds_802_11_sleep_params { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Sleep clock error in ppm */ + u16 error; + + /* Wakeup offset in usec */ + u16 offset; + + /* Clock stabilization time in usec */ + u16 stabletime; + + /* control periodic calibration */ + u8 calcontrol; + + /* control the use of external sleep clock */ + u8 externalsleepclk; + + /* reserved field, should be set to zero */ + u16 reserved; +}; + +struct cmd_ds_802_11_inactivity_timeout { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Inactivity timeout in msec */ + u16 timeout; +}; + +struct cmd_ds_802_11_rf_channel { + u16 action; + u16 currentchannel; + u16 rftype; + u16 reserved; + u8 channellist[32]; +}; + +struct cmd_ds_802_11_rssi { + /* weighting factor */ + u16 N; + + u16 reserved_0; + u16 reserved_1; + u16 reserved_2; +}; + +struct cmd_ds_802_11_rssi_rsp { + u16 SNR; + u16 noisefloor; + u16 avgSNR; + u16 avgnoisefloor; +}; + +struct cmd_ds_802_11_mac_address { + u16 action; + u8 macadd[ETH_ALEN]; +}; + +struct cmd_ds_802_11_rf_tx_power { + u16 action; + u16 currentlevel; +}; + +struct cmd_ds_802_11_rf_antenna { + u16 action; + + /* Number of antennas or 0xffff(diversity) */ + u16 antennamode; + +}; + +struct cmd_ds_802_11_ps_mode { + u16 action; + u16 nullpktinterval; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct PS_CMD_ConfirmSleep { + u16 command; + u16 size; + u16 seqnum; + u16 result; + + u16 action; + u16 reserved1; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct cmd_ds_802_11_data_rate { + u16 action; + u16 reserverd; + u8 datarate[G_SUPPORTED_RATES]; +}; + +struct cmd_ds_802_11_rate_adapt_rateset { + u16 action; + u16 enablehwauto; + u16 bitmap; +}; + +struct cmd_ds_802_11_ad_hoc_start { + u8 SSID[IW_ESSID_MAX_SIZE]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + union IEEEtypes_ssparamset ssparamset; + union ieeetypes_phyparamset phyparamset; + u16 probedelay; + struct ieeetypes_capinfo cap; + u8 datarate[G_SUPPORTED_RATES]; + u8 tlv_memory_size_pad[100]; +} __attribute__ ((packed)); + +struct adhoc_bssdesc { + u8 BSSID[6]; + u8 SSID[32]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + u8 timestamp[8]; + u8 localtime[8]; + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[G_SUPPORTED_RATES]; + + /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the + * Adhoc join command and will cause a binary layout mismatch with + * the firmware + */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_join { + struct adhoc_bssdesc bssdescriptor; + u16 failtimeout; + u16 probedelay; + +} __attribute__ ((packed)); + +struct cmd_ds_802_11_enable_rsn { + u16 action; + u16 enable; +}; + +struct MrvlIEtype_keyParamSet { + /* type ID */ + u16 type; + + /* length of Payload */ + u16 length; + + /* type of key: WEP=0, TKIP=1, AES=2 */ + u16 keytypeid; + + /* key control Info specific to a keytypeid */ + u16 keyinfo; + + /* length of key */ + u16 keylen; + + /* key material of size keylen */ + u8 key[32]; +}; + +struct cmd_ds_802_11_key_material { + u16 action; + struct MrvlIEtype_keyParamSet keyParamSet[2]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_eeprom_access { + u16 action; + + /* multiple 4 */ + u16 offset; + u16 bytecount; + u8 value; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_tpc_cfg { + u16 action; + u8 enable; + s8 P0; + s8 P1; + s8 P2; + u8 usesnr; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_led_ctrl { + u16 action; + u16 numled; + u8 data[256]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_pwr_cfg { + u16 action; + u8 enable; + s8 PA_P0; + s8 PA_P1; + s8 PA_P2; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_afc { + u16 afc_auto; + union { + struct { + u16 threshold; + u16 period; + }; + struct { + s16 timing_offset; + s16 carrier_offset; + }; + }; +} __attribute__ ((packed)); + +struct cmd_tx_rate_query { + u16 txrate; +} __attribute__ ((packed)); + +struct cmd_ds_get_tsf { + __le64 tsfvalue; +} __attribute__ ((packed)); + +struct cmd_ds_bt_access { + u16 action; + u32 id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; +} __attribute__ ((packed)); + +struct cmd_ds_fwt_access { + u16 action; + u32 id; + u8 da[ETH_ALEN]; + u8 dir; + u8 ra[ETH_ALEN]; + u32 ssn; + u32 dsn; + u32 metric; + u8 hopcount; + u8 ttl; + u32 expiration; + u8 sleepmode; + u32 snr; + u32 references; +} __attribute__ ((packed)); + +#define MESH_STATS_NUM 7 +struct cmd_ds_mesh_access { + u16 action; + u32 data[MESH_STATS_NUM + 1]; /* last position reserved */ +} __attribute__ ((packed)); + +struct cmd_ds_command { + /* command header */ + u16 command; + u16 size; + u16 seqnum; + u16 result; + + /* command Body */ + union { + struct cmd_ds_get_hw_spec hwspec; + struct cmd_ds_802_11_ps_mode psmode; + struct cmd_ds_802_11_scan scan; + struct cmd_ds_802_11_scan_rsp scanresp; + struct cmd_ds_mac_control macctrl; + struct cmd_ds_802_11_associate associate; + struct cmd_ds_802_11_deauthenticate deauth; + struct cmd_ds_802_11_set_wep wep; + struct cmd_ds_802_11_ad_hoc_start ads; + struct cmd_ds_802_11_reset reset; + struct cmd_ds_802_11_ad_hoc_result result; + struct cmd_ds_802_11_get_log glog; + struct cmd_ds_802_11_authenticate auth; + struct cmd_ds_802_11_get_stat gstat; + struct cmd_ds_802_3_get_stat gstat_8023; + struct cmd_ds_802_11_snmp_mib smib; + struct cmd_ds_802_11_rf_tx_power txp; + struct cmd_ds_802_11_rf_antenna rant; + struct cmd_ds_802_11_data_rate drate; + struct cmd_ds_802_11_rate_adapt_rateset rateset; + struct cmd_ds_mac_multicast_adr madr; + struct cmd_ds_802_11_ad_hoc_join adj; + struct cmd_ds_802_11_radio_control radio; + struct cmd_ds_802_11_rf_channel rfchannel; + struct cmd_ds_802_11_rssi rssi; + struct cmd_ds_802_11_rssi_rsp rssirsp; + struct cmd_ds_802_11_disassociate dassociate; + struct cmd_ds_802_11_mac_address macadd; + struct cmd_ds_802_11_enable_rsn enbrsn; + struct cmd_ds_802_11_key_material keymaterial; + struct cmd_ds_mac_reg_access macreg; + struct cmd_ds_bbp_reg_access bbpreg; + struct cmd_ds_rf_reg_access rfreg; + struct cmd_ds_802_11_eeprom_access rdeeprom; + + struct cmd_ds_802_11d_domain_info domaininfo; + struct cmd_ds_802_11d_domain_info domaininforesp; + + struct cmd_ds_802_11_sleep_params sleep_params; + struct cmd_ds_802_11_inactivity_timeout inactivity_timeout; + struct cmd_ds_802_11_tpc_cfg tpccfg; + struct cmd_ds_802_11_pwr_cfg pwrcfg; + struct cmd_ds_802_11_afc afc; + struct cmd_ds_802_11_led_ctrl ledgpio; + + struct cmd_tx_rate_query txrate; + struct cmd_ds_bt_access bt; + struct cmd_ds_fwt_access fwt; + struct cmd_ds_mesh_access mesh; + struct cmd_ds_get_tsf gettsf; + struct cmd_ds_802_11_subscribe_event subscribe_event; + } params; +} __attribute__ ((packed)); + +#endif diff --git a/drivers/net/wireless/libertas/if_bootcmd.c b/drivers/net/wireless/libertas/if_bootcmd.c new file mode 100644 index 00000000000..567000c3e87 --- /dev/null +++ b/drivers/net/wireless/libertas/if_bootcmd.c @@ -0,0 +1,38 @@ +/** + * This file contains functions used in USB Boot command + * and Boot2/FW update + */ + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +/** + * @brief This function issues Boot command to the Boot2 code + * @param ivalue 1:Boot from FW by USB-Download + * 2:Boot from FW in EEPROM + * @return 0 + */ +int if_usb_issue_boot_command(wlan_private *priv, int ivalue) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct bootcmdstr sbootcmd; + int i; + + /* Prepare command */ + sbootcmd.u32magicnumber = BOOT_CMD_MAGIC_NUMBER; + sbootcmd.u8cmd_tag = ivalue; + for (i=0; i<11; i++) + sbootcmd.au8dumy[i]=0x00; + memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr)); + + /* Issue command */ + usb_tx_block(priv, cardp->bulk_out_buffer, sizeof(struct bootcmdstr)); + + return 0; +} diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c new file mode 100644 index 00000000000..ae6f72a6cdf --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.c @@ -0,0 +1,951 @@ +/** + * This file contains functions used in USB interface module. + */ +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +#define MESSAGE_HEADER_LEN 4 + +static const char usbdriver_name[] = "usb8xxx"; + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { + USB_DEVICE(USB8388_VID_1, USB8388_PID_1), + }, + { + USB_DEVICE(USB8388_VID_2, USB8388_PID_2), + }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); + +/** + * @brief call back function to handle the status of the URB + * @param urb pointer to urb structure + * @return N/A + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + wlan_private *priv = (wlan_private *) (urb->context); + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + /* handle the transmission complete validations */ + + if (urb->status != 0) { + /* print the failure status number for debug */ + lbs_pr_info("URB in failure status\n"); + } else { + lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n"); + lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n", + urb->actual_length); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + /* Wake main thread if commands are pending */ + if (!adapter->cur_cmd) + wake_up_interruptible(&priv->mainthread.waitq); + if ((adapter->connect_status == libertas_connected)) + netif_wake_queue(dev); + } + + return; +} + +/** + * @brief free tx/rx urb, skb and rx buffer + * @param cardp pointer usb_card_rec + * @return N/A + */ +void if_usb_free(struct usb_card_rec *cardp) +{ + ENTER(); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + kfree(cardp->bulk_out_buffer); + cardp->bulk_out_buffer = NULL; + + LEAVE(); + return; +} + +/** + * @brief sets the configuration values + * @param ifnum interface number + * @param id pointer to usb_device_id + * @return 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + wlan_private *pwlanpriv; + struct usb_card_rec *usb_cardp; + int i; + + udev = interface_to_usbdev(intf); + + usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + if (!usb_cardp) { + lbs_pr_err("Out of memory allocating private data.\n"); + goto error; + } + + usb_cardp->udev = udev; + iface_desc = intf->cur_altsetting; + + lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + udev->descriptor.bcdUSB, + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk in endpoint */ + lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n", + endpoint->wMaxPacketSize); + if (! + (usb_cardp->rx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1, &udev->dev, + "Rx URB allocation failed\n"); + goto dealloc; + } + usb_cardp->rx_urb_recall = 0; + + usb_cardp->bulk_in_size = + endpoint->wMaxPacketSize; + usb_cardp->bulk_in_endpointAddr = + (endpoint-> + bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n", + endpoint->bEndpointAddress); + } + + if (((endpoint-> + bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + USB_DIR_OUT) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* We found bulk out endpoint */ + if (! + (usb_cardp->tx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1,&udev->dev, + "Tx URB allocation failed\n"); + goto dealloc; + } + + usb_cardp->bulk_out_size = + endpoint->wMaxPacketSize; + lbs_dev_dbg(1, &udev->dev, + "Bulk out size is %d\n", + endpoint->wMaxPacketSize); + usb_cardp->bulk_out_endpointAddr = + endpoint->bEndpointAddress; + lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n", + endpoint->bEndpointAddress); + usb_cardp->bulk_out_buffer = + kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, + GFP_KERNEL); + + if (!usb_cardp->bulk_out_buffer) { + lbs_dev_dbg(1, &udev->dev, + "Could not allocate buffer\n"); + goto dealloc; + } + } + } + + + /* At this point wlan_add_card() will be called. Don't worry + * about keeping pwlanpriv around since it will be set on our + * usb device data in -> add() -> libertas_sbi_register_dev(). + */ + if (!(pwlanpriv = wlan_add_card(usb_cardp))) + goto dealloc; + + usb_get_dev(udev); + usb_set_intfdata(intf, usb_cardp); + + /* + * return card structure, which can be got back in the + * diconnect function as the ptr + * argument. + */ + return 0; + +dealloc: + if_usb_free(usb_cardp); + +error: + return -ENOMEM; +} + +/** + * @brief free resource and cleanup + * @param udev pointer to usb_device + * @param ptr pointer to usb_cardp + * @return N/A + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = (wlan_private *) cardp->priv; + wlan_adapter *adapter = NULL; + + adapter = priv->adapter; + + /* + * Update Surprise removed to TRUE + */ + adapter->surpriseremoved = 1; + + /* card is removed and we can call wlan_remove_card */ + lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n"); + wlan_remove_card(cardp); + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + + return; +} + +/** + * @brief This function download FW + * @param priv pointer to wlan_private + * @return 0 + */ +static int if_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct FWData *fwdata; + struct fwheader *fwheader; + u8 *firmware = priv->firmware->data; + + fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC); + + if (!fwdata) + return -1; + + fwheader = &fwdata->fwheader; + + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum = cardp->lastseqnum - 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n", + cardp->totalbytes); + + memcpy(fwheader, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n"); + memcpy(fwdata->data, &firmware[cardp->totalbytes], + fwdata->fwheader.datalength); + + lbs_dev_dbg(2, &cardp->udev->dev, + "Data length = %d\n", fwdata->fwheader.datalength); + + cardp->fwseqnum = cardp->fwseqnum + 1; + + fwdata->seqnum = cardp->fwseqnum; + cardp->lastseqnum = fwdata->seqnum; + cardp->totalbytes += fwdata->fwheader.datalength; + + if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) { + lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, + cardp->totalbytes); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + + } else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) { + lbs_dev_dbg(2, &cardp->udev->dev, + "Host has finished FW downloading\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "Donwloading FW JUMP BLOCK\n"); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + cardp->fwfinalblk = 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, + "The firmware download is done size is %d\n", + cardp->totalbytes); + + kfree(fwdata); + + return 0; +} + +static int libertas_do_reset(wlan_private *priv) +{ + int ret; + struct usb_card_rec *cardp = priv->wlan_dev.card; + + ret = usb_reset_device(cardp->udev); + if (!ret) { + msleep(10); + reset_device(priv); + msleep(10); + } + return ret; +} + +/** + * @brief This function transfer the data to the device. + * @param priv pointer to wlan_private + * @param payload pointer to payload data + * @param nb data length + * @return 0 or -1 + */ +int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb) +{ + /* pointer to card structure */ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int ret = -1; + + /* check if device is removed */ + if (priv->adapter->surpriseremoved) { + lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n"); + goto tx_ret; + } + + usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->bulk_out_endpointAddr), + payload, nb, if_usb_write_bulk_callback, priv); + + cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { + /* transfer failed */ + lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n"); + ret = 0; + } + +tx_ret: + return ret; +} + +static int __if_usb_submit_rx_urb(wlan_private * priv, + void (*callbackfn) + (struct urb *urb)) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct sk_buff *skb; + struct read_cb_info *rinfo = &cardp->rinfo; + int ret = -1; + + if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { + lbs_pr_err("No free skb\n"); + goto rx_ret; + } + + rinfo->skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, + cardp->bulk_in_endpointAddr), + (void *) (skb->tail + (size_t) IPFIELD_ALIGN_OFFSET), + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, + rinfo); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); + if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { + /* handle failure conditions */ + lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n"); + ret = 0; + } + +rx_ret: + return ret; +} + +static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload); +} + +static inline int if_usb_submit_rx_urb(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + struct fwsyncheader *syncfwheader; + struct bootcmdrespStr bootcmdresp; + + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed during fw load\n"); + kfree_skb(skb); + return; + } + + if (cardp->bootcmdresp == 0) { + memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(bootcmdresp)); + if (cardp->udev->descriptor.bcdDevice < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + return; + } + if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) { + lbs_pr_info( + "boot cmd response wrong magic number (0x%x)\n", + bootcmdresp.u32magicnumber); + } else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) { + lbs_pr_info( + "boot cmd response cmd_tag error (%d)\n", + bootcmdresp.u8cmd_tag); + } else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) { + lbs_pr_info( + "boot cmd response result error (%d)\n", + bootcmdresp.u8result); + } else { + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + } + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + return; + } + + syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); + if (!syncfwheader) { + lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n"); + kfree_skb(skb); + return; + } + + memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(struct fwsyncheader)); + + if (!syncfwheader->cmd) { + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk with correct CRC\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk seqnum = %d\n", + syncfwheader->seqnum); + cardp->CRC_OK = 1; + } else { + lbs_dev_dbg(1, &cardp->udev->dev, + "FW received Blk with CRC error\n"); + cardp->CRC_OK = 0; + } + + kfree_skb(skb); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_prog_firmware(priv); + + if_usb_submit_rx_urb_fwload(priv); +exit: + kfree(syncfwheader); + + return; + +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + + MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { + lbs_dev_dbg(1, &cardp->udev->dev, + "Packet length is Invalid\n"); + kfree_skb(skb); + return; + } + + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + libertas_process_rxed_packet(priv, skb); + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); +} + +static inline void process_cmdrequest(int recvlength, u8 *recvbuff, + struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + u8 *cmdbuf; + if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) { + lbs_dev_dbg(1, &cardp->udev->dev, + "The receive buffer is too large\n"); + kfree_skb(skb); + return; + } + + if (!in_interrupt()) + BUG(); + + spin_lock(&priv->adapter->driver_lock); + /* take care of cur_cmd = NULL case by reading the + * data to clear the interrupt */ + if (!priv->adapter->cur_cmd) { + cmdbuf = priv->wlan_dev.upld_buf; + priv->adapter->hisregcpy &= ~his_cmdupldrdy; + } else + cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr; + + cardp->usb_int_cause |= his_cmdupldrdy; + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); + memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN, + priv->wlan_dev.upld_len); + + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + + lbs_dev_dbg(1, &cardp->udev->dev, + "Wake up main thread to handle cmd response\n"); + + return; +} + +/** + * @brief This function reads of the packet into the upload buff, + * wake up the main thread and initialise the Rx callack. + * + * @param urb pointer to struct urb + * @return N/A + */ +static void if_usb_receive(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + int recvlength = urb->actual_length; + u8 *recvbuff = NULL; + u32 recvtype; + + ENTER(); + + if (recvlength) { + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed\n"); + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; + memcpy(&recvtype, recvbuff, sizeof(u32)); + lbs_dev_dbg(1, &cardp->udev->dev, + "Recv length = 0x%x\n", recvlength); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type = 0x%X\n", recvtype); + recvtype = le32_to_cpu(recvtype); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type after = 0x%X\n", recvtype); + } else if (urb->status) + goto rx_exit; + + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + /* Event cause handling */ + spin_lock(&priv->adapter->driver_lock); + cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN); + lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n", + cardp->usb_event_cause); + if (cardp->usb_event_cause & 0xffff0000) { + libertas_send_tx_feedback(priv); + spin_unlock(&priv->adapter->driver_lock); + break; + } + cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3; + cardp->usb_int_cause |= his_cardevent; + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + goto rx_exit; + default: + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(priv); +rx_exit: + LEAVE(); + return; +} + +/** + * @brief This function downloads data to FW + * @param priv pointer to wlan_private structure + * @param type type of data + * @param buf pointer to data buffer + * @param len number of bytes + * @return 0 or -1 + */ +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb) +{ + int ret = -1; + u32 tmp; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type); + lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb); + + if (type == MVMS_CMD) { + tmp = cpu_to_le32(CMD_TYPE_REQUEST); + priv->wlan_dev.dnld_sent = DNLD_CMD_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + + } else { + tmp = cpu_to_le32(CMD_TYPE_DATA); + priv->wlan_dev.dnld_sent = DNLD_DATA_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + } + + memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb); + + ret = + usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN); + + return ret; +} + +/* called with adapter->driver_lock held */ +int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + + *ireg = cardp->usb_int_cause; + cardp->usb_int_cause = 0; + + lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg); + + return 0; +} + +int libertas_sbi_read_event_cause(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + priv->adapter->eventcause = cardp->usb_event_cause; + /* Re-submit rx urb here to avoid event lost issue */ + if_usb_submit_rx_urb(priv); + return 0; +} + +int reset_device(wlan_private *priv) +{ + int ret; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset, + cmd_act_halt, 0, 0, NULL); + msleep_interruptible(10); + + return ret; +} + +int libertas_sbi_unregister_dev(wlan_private * priv) +{ + int ret = 0; + + /* Need to send a Reset command to device before USB resources freed + * and wlan_remove_card() called, then device can handle FW download + * again. + */ + if (priv) + reset_device(priv); + + return ret; +} + + +/** + * @brief This function register usb device and initialize parameter + * @param priv pointer to wlan_private + * @return 0 or -1 + */ +int libertas_sbi_register_dev(wlan_private * priv) +{ + + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + ENTER(); + + cardp->priv = priv; + cardp->eth_dev = priv->wlan_dev.netdev; + priv->hotplug_device = &(cardp->udev->dev); + + SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev)); + + lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n", + cardp->udev); + + LEAVE(); + return 0; +} + + + +int libertas_sbi_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int i = 0; + static int reset_count = 10; + + ENTER(); + + cardp->rinfo.priv = priv; + +restart: + if (if_usb_submit_rx_urb_fwload(priv) < 0) { + lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n"); + LEAVE(); + return -1; + } + + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + /* Issue Boot command = 1, Boot from Download-FW */ + if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp == 0) { + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + return -1; + } + + i = 0; + priv->adapter->fw_ready = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + if_prog_firmware(priv); + + do { + lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n"); + i++; + msleep_interruptible(100); + if (priv->adapter->surpriseremoved || i >= 20) + break; + } while (!cardp->fwdnldover); + + if (!cardp->fwdnldover) { + lbs_pr_info("failed to load fw, resetting device!\n"); + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + + lbs_pr_info("FW download failure, time = %d ms\n", i * 100); + LEAVE(); + return -1; + } + + if_usb_submit_rx_urb(priv); + + /* Delay 200 ms to waiting for the FW ready */ + msleep_interruptible(200); + + priv->adapter->fw_ready = 1; + + LEAVE(); + return 0; +} + +/** + * @brief Given a usb_card_rec return its wlan_private + * @param card pointer to a usb_card_rec + * @return pointer to wlan_private + */ +wlan_private *libertas_sbi_get_priv(void *card) +{ + struct usb_card_rec *cardp = card; + return cardp->priv; +} + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private * priv) +{ + return 0; +} + +int libertas_sbi_resume(wlan_private * priv) +{ + return 0; +} +#endif + +#ifdef CONFIG_PM +static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = cardp->priv; + + ENTER(); + + if (priv->adapter->psstate != PS_STATE_FULL_POWER) + return -1; + + netif_device_detach(cardp->eth_dev); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + cardp->rx_urb_recall = 1; + + LEAVE(); + return 0; +} + +static int if_usb_resume(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + + ENTER(); + + cardp->rx_urb_recall = 0; + + if_usb_submit_rx_urb(cardp->priv); + + netif_device_attach(cardp->eth_dev); + + LEAVE(); + return 0; +} +#else +#define if_usb_suspend NULL +#define if_usb_resume NULL +#endif + +static struct usb_driver if_usb_driver = { + /* driver name */ + .name = usbdriver_name, + /* probe function name */ + .probe = if_usb_probe, + /* disconnect function name */ + .disconnect = if_usb_disconnect, + /* device signature table */ + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, +}; + +/** + * @brief This function registers driver. + * @param add pointer to add_card callback function + * @param remove pointer to remove card callback function + * @param arg pointer to call back function parameter + * @return dummy success variable + */ +int libertas_sbi_register(void) +{ + /* + * API registers the Marvell USB driver + * to the USB system + */ + usb_register(&if_usb_driver); + + /* Return success to wlan layer */ + return 0; +} + +/** + * @brief This function removes usb driver. + * @return N/A + */ +void libertas_sbi_unregister(void) +{ + /* API unregisters the driver from USB subsystem */ + usb_deregister(&if_usb_driver); + return; +} diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h new file mode 100644 index 00000000000..170dfe6809f --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.h @@ -0,0 +1,107 @@ +/** + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define IPFIELD_ALIGN_OFFSET 2 + +#define USB8388_VID_1 0x1286 +#define USB8388_PID_1 0x2001 +#define USB8388_VID_2 0x05a3 +#define USB8388_PID_2 0x8388 + +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */ + +struct bootcmdstr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 au8dumy[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 + +struct bootcmdrespStr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 u8result; + u8 au8dumy[2]; +}; + +/* read callback private data */ +struct read_cb_info { + wlan_private *priv; + struct sk_buff *skb; +}; + +/** USB card description structure*/ +struct usb_card_rec { + struct net_device *eth_dev; + struct usb_device *udev; + struct urb *rx_urb, *tx_urb; + void *priv; + struct read_cb_info rinfo; + + int bulk_in_size; + u8 bulk_in_endpointAddr; + + u8 *bulk_out_buffer; + int bulk_out_size; + u8 bulk_out_endpointAddr; + + u8 CRC_OK; + u32 fwseqnum; + u32 lastseqnum; + u32 totalbytes; + u32 fwlastblksent; + u8 fwdnldover; + u8 fwfinalblk; + + u32 usb_event_cause; + u8 usb_int_cause; + + u8 rx_urb_recall; + + u8 bootcmdresp; +}; + +/** fwheader */ +struct fwheader { + u32 dnldcmd; + u32 baseaddr; + u32 datalength; + u32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/** FWData */ +struct FWData { + struct fwheader fwheader; + u32 seqnum; + u8 data[FW_MAX_DATA_BLK_SIZE]; +}; + +/** fwsyncheader */ +struct fwsyncheader { + u32 cmd; + u32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 + +#define FW_DATA_XMIT_SIZE \ + sizeof(struct fwheader) + fwdata->fwheader.datalength + sizeof(u32) + +int usb_tx_block(wlan_private *priv, u8 *payload, u16 nb); +void if_usb_free(struct usb_card_rec *cardp); +int if_usb_issue_boot_command(wlan_private *priv, int ivalue); + diff --git a/drivers/net/wireless/libertas/ioctl.c b/drivers/net/wireless/libertas/ioctl.c new file mode 100644 index 00000000000..a8f76c35899 --- /dev/null +++ b/drivers/net/wireless/libertas/ioctl.c @@ -0,0 +1,991 @@ +/** + * This file contains ioctl functions + */ + +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> + +#include <net/iw_handler.h> +#include <net/ieee80211.h> + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN + \ + IW_ESSID_MAX_SIZE + \ + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \ + IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \ + IW_EV_PARAM_LEN + 40) /* 40 for WPAIE */ + +#define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ) + +static int wlan_set_region(wlan_private * priv, u16 region_code) +{ + int i; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + // use the region code to search for the index + if (region_code == libertas_region_code_to_index[i]) { + priv->adapter->regiontableindex = (u16) i; + priv->adapter->regioncode = region_code; + break; + } + } + + // if it's unidentified region code + if (i >= MRVDRV_MAX_REGION_CODE) { + lbs_pr_debug(1, "region Code not identified\n"); + LEAVE(); + return -1; + } + + if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) { + LEAVE(); + return -EINVAL; + } + + return 0; +} + +static inline int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + return -1; +} + +/* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx") + into binary format (6 bytes). + + This function expects that each byte is represented with 2 characters + (e.g., 11:2:11:11:11:11 is invalid) + + */ +static char *eth_str2addr(char *ethstr, u8 * addr) +{ + int i, val, val2; + char *pos = ethstr; + + /* get rid of initial blanks */ + while (*pos == ' ' || *pos == '\t') + ++pos; + + for (i = 0; i < 6; i++) { + val = hex2int(*pos++); + if (val < 0) + return NULL; + val2 = hex2int(*pos++); + if (val2 < 0) + return NULL; + addr[i] = (val * 16 + val2) & 0xff; + + if (i < 5 && *pos++ != ':') + return NULL; + } + return pos; +} + +/* this writes xx:xx:xx:xx:xx:xx into ethstr + (ethstr must have space for 18 chars) */ +static int eth_addr2str(u8 * addr, char *ethstr) +{ + int i; + char *pos = ethstr; + + for (i = 0; i < 6; i++) { + sprintf(pos, "%02x", addr[i] & 0xff); + pos += 2; + if (i < 5) + *pos++ = ':'; + } + return 17; +} + +/** + * @brief Add an entry to the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + char *pos; + u8 ethaddr[ETH_ALEN]; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("BT_ADD: Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str); + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_add, + cmd_option_waitforrsp, 0, ethaddr)); +} + +/** + * @brief Delete an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + u8 ethaddr[ETH_ALEN]; + char *pos; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_del, + cmd_option_waitforrsp, 0, ethaddr)); + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the BT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_reset_ioctl(wlan_private * priv) +{ + ENTER(); + + lbs_pr_alert( "BT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_reset, + cmd_option_waitforrsp, 0, NULL)); + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + int pos; + char *addr1; + struct iwreq *wrq = (struct iwreq *)req; + /* used to pass id and store the bt entry returned by the FW */ + union { + int id; + char addr1addr2[2 * ETH_ALEN]; + } param; + static char outstr[64]; + char *pbuf = outstr; + int ret; + + ENTER(); + + if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -1; + } + param.id = simple_strtoul(outstr, NULL, 10); + pos = sprintf(pbuf, "%d: ", param.id); + pbuf += pos; + + ret = libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_list, + cmd_option_waitforrsp, 0, + (char *)¶m); + + if (ret == 0) { + addr1 = param.addr1addr2; + + pos = sprintf(pbuf, "ignoring traffic from "); + pbuf += pos; + pos = eth_addr2str(addr1, pbuf); + pbuf += pos; + } else { + sprintf(pbuf, "(null)"); + pbuf += pos; + } + + wrq->u.data.length = strlen(outstr); + if (copy_to_user(wrq->u.data.pointer, (char *)outstr, + wrq->u.data.length)) { + lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Find the next parameter in an input string + * @param ptr A pointer to the input parameter string + * @return A pointer to the next parameter, or 0 if no parameters left. + */ +static char * next_param(char * ptr) +{ + if (!ptr) return NULL; + while (*ptr == ' ' || *ptr == '\t') ++ptr; + return (*ptr == '\0') ? NULL : ptr; +} + +/** + * @brief Add an entry to the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[128]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.metric = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.metric = FWT_DEFAULT_METRIC; + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + + if ((ptr = next_param(ptr))) + fwt_access.ssn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.ssn = FWT_DEFAULT_SSN; + + if ((ptr = next_param(ptr))) + fwt_access.dsn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.dsn = FWT_DEFAULT_DSN; + + if ((ptr = next_param(ptr))) + fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT; + + if ((ptr = next_param(ptr))) + fwt_access.ttl = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.ttl = FWT_DEFAULT_TTL; + + if ((ptr = next_param(ptr))) + fwt_access.expiration = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.expiration = FWT_DEFAULT_EXPIRATION; + + if ((ptr = next_param(ptr))) + fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE; + + if ((ptr = next_param(ptr))) + fwt_access.snr = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.snr = FWT_DEFAULT_SNR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str, + fwt_access.dir, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n", + fwt_access.ssn, fwt_access.dsn, fwt_access.metric, + fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration, + fwt_access.sleepmode, fwt_access.snr); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_add, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + +/** + * @brief Delete an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str, + ethaddr2_str, fwt_access.dir); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_del, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + + +/** + * @brief Print route parameters + * @param fwt_access struct cmd_ds_fwt_access with route info + * @param buf destination buffer for route info + */ +static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf) +{ + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.da, buf); + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.ra, buf); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric)); + buf += sprintf(buf, " %u", fwt_access.dir); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn)); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn)); + buf += sprintf(buf, " %u", fwt_access.hopcount); + buf += sprintf(buf, " %u", fwt_access.ttl); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration)); + buf += sprintf(buf, " %u", fwt_access.sleepmode); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr)); +} + +/** + * @brief Lookup an entry in the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + char *ptr; + static struct cmd_ds_fwt_access fwt_access; + static char out_str[128]; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n"); + return -EINVAL; + } + +#ifdef DEBUG + { + char ethaddr1_str[18]; + lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str); + } +#endif + + ret = libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_lookup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, out_str); + else + sprintf(out_str, "(null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the FWT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_reset_ioctl(wlan_private * priv) +{ + lbs_pr_debug(1, "FWT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_reset, + cmd_option_waitforrsp, 0, NULL)); +} + +/** + * @brief List an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, pbuf); + else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FRT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_route, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " "); + pbuf += eth_addr2str(fwt_access.da, pbuf); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric)); + pbuf += sprintf(pbuf, " %u", fwt_access.dir); + /* note that the firmware returns the nid in the id field */ + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn)); + pbuf += sprintf(pbuf, " hop %u", fwt_access.hopcount); + pbuf += sprintf(pbuf, " ttl %u", fwt_access.ttl); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FNT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + memset(&fwt_access, 0, sizeof(fwt_access)); + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_neighbor, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " ra "); + pbuf += eth_addr2str(fwt_access.ra, pbuf); + pbuf += sprintf(pbuf, " slp %u", fwt_access.sleepmode); + pbuf += sprintf(pbuf, " snr %u", le32_to_cpu(fwt_access.snr)); + pbuf += sprintf(pbuf, " ref %u", le32_to_cpu(fwt_access.references)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Cleans up the route (FRT) and neighbor (FNT) tables + * (Garbage Collection) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: cleaning up\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_cleanup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + wrq->u.param.value = le32_to_cpu(fwt_access.references); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets firmware internal time (debug purposes) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: getting time\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_time, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + wrq->u.param.value = le32_to_cpu(fwt_access.references); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + memset(&mesh_access, 0, sizeof(mesh_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_get_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret == 0) + wrq->u.param.value = le32_to_cpu(mesh_access.data[0]); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param ttl New ttl value + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl) +{ + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + if( (ttl > 0xff) || (ttl < 0) ) + return -EINVAL; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = ttl; + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_set_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret != 0) + ret = -EFAULT; + + 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 0--success, otherwise fail + */ +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int subcmd = 0; + int idata = 0; + int *pdata; + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iwreq *wrq = (struct iwreq *)req; + + ENTER(); + + lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WLAN_SETNONE_GETNONE: /* set WPA mode on/off ioctl #20 */ + switch (wrq->u.data.flags) { + case WLAN_SUBCMD_BT_RESET: /* bt_reset */ + wlan_bt_reset_ioctl(priv); + break; + case WLAN_SUBCMD_FWT_RESET: /* fwt_reset */ + wlan_fwt_reset_ioctl(priv); + break; + } /* End of switch */ + break; + + case WLAN_SETONEINT_GETNONE: + /* The first 4 bytes of req->ifr_data is sub-ioctl number + * after 4 bytes sits the payload. + */ + subcmd = wrq->u.data.flags; + if (!subcmd) + subcmd = (int)wrq->u.param.value; + + switch (subcmd) { + case WLANSETREGION: + idata = SUBCMD_DATA(wrq); + ret = wlan_set_region(priv, (u16) idata); + break; + case WLAN_SUBCMD_MESH_SET_TTL: + idata = SUBCMD_DATA(wrq); + ret = wlan_mesh_set_ttl_ioctl(priv, idata); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + break; + + case WLAN_SET128CHAR_GET128CHAR: + switch ((int)wrq->u.data.flags) { + case WLAN_SUBCMD_BT_ADD: + ret = wlan_bt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_DEL: + ret = wlan_bt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_LIST: + ret = wlan_bt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_ADD: + ret = wlan_fwt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_DEL: + ret = wlan_fwt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LOOKUP: + ret = wlan_fwt_lookup_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_NEIGHBOR: + ret = wlan_fwt_list_neighbor_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST: + ret = wlan_fwt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_ROUTE: + ret = wlan_fwt_list_route_ioctl(priv, req); + break; + } + break; + + case WLAN_SETNONE_GETONEINT: + switch (wrq->u.param.value) { + case WLANGETREGION: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->regioncode; + break; + case WLAN_SUBCMD_FWT_CLEANUP: /* fwt_cleanup */ + ret = wlan_fwt_cleanup_ioctl(priv, req); + break; + + case WLAN_SUBCMD_FWT_TIME: /* fwt_time */ + ret = wlan_fwt_time_ioctl(priv, req); + break; + + case WLAN_SUBCMD_MESH_GET_TTL: + ret = wlan_mesh_get_ttl_ioctl(priv, req); + break; + + default: + ret = -EOPNOTSUPP; + + } + + break; + + case WLAN_SET_GET_SIXTEEN_INT: + switch ((int)wrq->u.data.flags) { + case WLAN_LED_GPIO_CTRL: + { + int i; + int data[16]; + + struct cmd_ds_802_11_led_ctrl ctrl; + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio *) ctrl.data; + + memset(&ctrl, 0, sizeof(ctrl)); + if (wrq->u.data.length > MAX_LEDS * 2) + return -ENOTSUPP; + if ((wrq->u.data.length % 2) != 0) + return -ENOTSUPP; + if (wrq->u.data.length == 0) { + ctrl.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * + wrq->u.data.length)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + ctrl.action = + cpu_to_le16 + (cmd_act_set); + ctrl.numled = cpu_to_le16(0); + gpio->header.type = + cpu_to_le16(TLV_TYPE_LED_GPIO); + gpio->header.len = wrq->u.data.length; + for (i = 0; i < wrq->u.data.length; + i += 2) { + gpio->ledpin[i / 2].led = + data[i]; + gpio->ledpin[i / 2].pin = + data[i + 1]; + } + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_led_gpio_ctrl, + 0, + cmd_option_waitforrsp, + 0, (void *)&ctrl); + for (i = 0; i < gpio->header.len; i += 2) { + data[i] = gpio->ledpin[i / 2].led; + data[i + 1] = gpio->ledpin[i / 2].pin; + } + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * + gpio->header.len)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = gpio->header.len; + } + break; + } + break; + + default: + ret = -EINVAL; + break; + } + LEAVE(); + return ret; +} + + diff --git a/drivers/net/wireless/libertas/join.c b/drivers/net/wireless/libertas/join.c new file mode 100644 index 00000000000..d4926b83e14 --- /dev/null +++ b/drivers/net/wireless/libertas/join.c @@ -0,0 +1,929 @@ +/** + * Functions implementing wlan infrastructure and adhoc join routines, + * IOCTL handlers as well as command preperation and response routines + * for sending adhoc start, adhoc join, and association commands + * to the firmware. + */ +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "decl.h" +#include "join.h" +#include "dev.h" + +#define AD_HOC_CAP_PRIVACY_ON 1 + +/** + * @brief This function finds out the common rates between rate1 and rate2. + * + * It will fill common rates in rate1 as output if found. + * + * NOTE: Setting the MSB of the basic rates need to be taken + * care, either before or after calling this function + * + * @param adapter A pointer to wlan_adapter structure + * @param rate1 the buffer which keeps input and output + * @param rate1_size the size of rate1 buffer + * @param rate2 the buffer which keeps rate2 + * @param rate2_size the size of rate2 buffer. + * + * @return 0 or -1 + */ +static int get_common_rates(wlan_adapter * adapter, u8 * rate1, + int rate1_size, u8 * rate2, int rate2_size) +{ + u8 *ptr = rate1; + int ret = 0; + u8 tmp[30]; + int i; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp, rate1, min_t(size_t, rate1_size, sizeof(tmp))); + memset(rate1, 0, rate1_size); + + /* Mask the top bit of the original values */ + for (i = 0; tmp[i] && i < sizeof(tmp); i++) + tmp[i] &= 0x7F; + + for (i = 0; rate2[i] && i < rate2_size; i++) { + /* Check for Card Rate in tmp, excluding the top bit */ + if (strchr(tmp, rate2[i] & 0x7F)) { + /* values match, so copy the Card Rate to rate1 */ + *rate1++ = rate2[i]; + } + } + + lbs_dbg_hex("rate1 (AP) rates:", tmp, sizeof(tmp)); + lbs_dbg_hex("rate2 (Card) rates:", rate2, rate2_size); + lbs_dbg_hex("Common rates:", ptr, rate1_size); + lbs_pr_debug(1, "Tx datarate is set to 0x%X\n", adapter->datarate); + + if (!adapter->is_datarate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == adapter->datarate) { + ret = 0; + goto done; + } + ptr++; + } + lbs_pr_alert( "Previously set fixed data rate %#x isn't " + "compatible with the network.\n", adapter->datarate); + + ret = -1; + goto done; + } + + ret = 0; +done: + return ret; +} + +int libertas_send_deauth(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->mode == IW_MODE_INFRA && + adapter->connect_status == libertas_connected) + ret = libertas_send_deauthentication(priv); + else + ret = -ENOTSUPP; + + return ret; +} + +/** + * @brief Associate to a specific BSS discovered in a scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to the BSS descriptor to associate with. + * + * @return 0-success, otherwise fail + */ +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_authenticate, + 0, cmd_option_waitforrsp, + 0, pbssdesc->macaddress); + + if (ret) { + LEAVE(); + return ret; + } + + /* set preamble to firmware */ + if (adapter->capinfo.shortpreamble && pbssdesc->cap.shortpreamble) + adapter->preamble = cmd_type_short_preamble; + else + adapter->preamble = cmd_type_long_preamble; + + libertas_set_radio_control(priv); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_associate, + 0, cmd_option_waitforrsp, 0, pbssdesc); + + LEAVE(); + return ret; +} + +/** + * @brief Start an Adhoc Network + * + * @param priv A pointer to wlan_private structure + * @param adhocssid The ssid of the Adhoc Network + * @return 0--success, -1--fail + */ +int libertas_start_adhoc_network(wlan_private * priv, struct WLAN_802_11_SSID *adhocssid) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + adapter->adhoccreate = 1; + + if (!adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocStart: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocStart: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "Adhoc channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %d\n", adapter->curbssparams.band); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_start, + 0, cmd_option_waitforrsp, 0, adhocssid); + + return ret; +} + +/** + * @brief Join an adhoc network found in a previous scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to a BSS descriptor found in a previous scan + * to attempt to join + * + * @return 0--success, -1--fail + */ +int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid =%s\n", + adapter->curbssparams.ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid_len =%u\n", + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid =%s\n", pbssdesc->ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid len =%u\n", + pbssdesc->ssid.ssidlength); + + /* check if the requested SSID is already joined */ + if (adapter->curbssparams.ssid.ssidlength + && !libertas_SSID_cmp(&pbssdesc->ssid, &adapter->curbssparams.ssid) + && (adapter->mode == IW_MODE_ADHOC)) { + + lbs_pr_debug(1, + "ADHOC_J_CMD: New ad-hoc SSID is the same as current, " + "not attempting to re-join"); + + return -1; + } + + /*Use shortpreamble only when both creator and card supports + short preamble */ + if (!pbssdesc->cap.shortpreamble || !adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocJoin: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocJoin: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %c\n", adapter->curbssparams.band); + + adapter->adhoccreate = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_join, + 0, cmd_option_waitforrsp, + OID_802_11_SSID, pbssdesc); + + return ret; +} + +int libertas_stop_adhoc_network(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_stop, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief Send Deauthentication Request + * + * @param priv A pointer to wlan_private structure + * @return 0--success, -1--fail + */ +int libertas_send_deauthentication(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_deauthenticate, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief This function prepares command of authenticate. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure + * @param pdata_buf Void cast of pointer to a BSSID to authenticate with + * + * @return 0 or -1 + */ +int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth; + int ret = -1; + u8 *bssid = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_authenticate); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate) + + S_DS_GEN); + + /* translate auth mode to 802.11 defined wire value */ + switch (adapter->secinfo.auth_mode) { + case IW_AUTH_ALG_OPEN_SYSTEM: + pauthenticate->authtype = 0x00; + break; + case IW_AUTH_ALG_SHARED_KEY: + pauthenticate->authtype = 0x01; + break; + case IW_AUTH_ALG_LEAP: + pauthenticate->authtype = 0x80; + break; + default: + lbs_pr_debug(1, "AUTH_CMD: invalid auth alg 0x%X\n", + adapter->secinfo.auth_mode); + goto out; + } + + memcpy(pauthenticate->macaddr, bssid, ETH_ALEN); + + lbs_pr_debug(1, "AUTH_CMD: Bssid is : %x:%x:%x:%x:%x:%x\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); + ret = 0; + +out: + return ret; +} + +int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_deauthenticate); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) + + S_DS_GEN); + + /* set AP MAC address */ + memmove(dauth->macaddr, adapter->curbssparams.bssid, + ETH_ALEN); + + /* Reason code 3 = Station is leaving */ +#define REASON_CODE_STA_LEAVING 3 + dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING); + + LEAVE(); + return 0; +} + +int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_associate *passo = &cmd->params.associate; + int ret = 0; + struct bss_descriptor *pbssdesc; + u8 *card_rates; + u8 *pos; + int card_rates_size; + u16 tmpcap; + struct mrvlietypes_ssidparamset *ssid; + struct mrvlietypes_phyparamset *phy; + struct mrvlietypes_ssparamset *ss; + struct mrvlietypes_ratesparamset *rates; + struct mrvlietypes_rsnparamset *rsn; + + ENTER(); + + pbssdesc = pdata_buf; + pos = (u8 *) passo; + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_associate); + + /* Save so we know which BSS Desc to use in the response handler */ + adapter->pattemptedbssdesc = pbssdesc; + + memcpy(passo->peerstaaddr, + pbssdesc->macaddress, sizeof(passo->peerstaaddr)); + pos += sizeof(passo->peerstaaddr); + + /* set the listen interval */ + passo->listeninterval = adapter->listeninterval; + + pos += sizeof(passo->capinfo); + pos += sizeof(passo->listeninterval); + pos += sizeof(passo->bcnperiod); + pos += sizeof(passo->dtimperiod); + + ssid = (struct mrvlietypes_ssidparamset *) pos; + ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid->header.len = pbssdesc->ssid.ssidlength; + memcpy(ssid->ssid, pbssdesc->ssid.ssid, ssid->header.len); + pos += sizeof(ssid->header) + ssid->header.len; + ssid->header.len = cpu_to_le16(ssid->header.len); + + phy = (struct mrvlietypes_phyparamset *) pos; + phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); + phy->header.len = sizeof(phy->fh_ds.dsparamset); + memcpy(&phy->fh_ds.dsparamset, + &pbssdesc->phyparamset.dsparamset.currentchan, + sizeof(phy->fh_ds.dsparamset)); + pos += sizeof(phy->header) + phy->header.len; + phy->header.len = cpu_to_le16(phy->header.len); + + ss = (struct mrvlietypes_ssparamset *) pos; + ss->header.type = cpu_to_le16(TLV_TYPE_CF); + ss->header.len = sizeof(ss->cf_ibss.cfparamset); + pos += sizeof(ss->header) + ss->header.len; + ss->header.len = cpu_to_le16(ss->header.len); + + rates = (struct mrvlietypes_ratesparamset *) pos; + rates->header.type = cpu_to_le16(TLV_TYPE_RATES); + + memcpy(&rates->rates, &pbssdesc->libertas_supported_rates, WLAN_SUPPORTED_RATES); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + if (get_common_rates(adapter, rates->rates, WLAN_SUPPORTED_RATES, + card_rates, card_rates_size)) { + ret = -1; + goto done; + } + + rates->header.len = min_t(size_t, strlen(rates->rates), WLAN_SUPPORTED_RATES); + adapter->curbssparams.numofrates = rates->header.len; + + pos += sizeof(rates->header) + rates->header.len; + rates->header.len = cpu_to_le16(rates->header.len); + + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + rsn = (struct mrvlietypes_rsnparamset *) pos; + rsn->header.type = (u16) adapter->wpa_ie[0]; /* WPA_IE or WPA2_IE */ + rsn->header.type = cpu_to_le16(rsn->header.type); + rsn->header.len = (u16) adapter->wpa_ie[1]; + memcpy(rsn->rsnie, &adapter->wpa_ie[2], rsn->header.len); + lbs_dbg_hex("ASSOC_CMD: RSN IE", (u8 *) rsn, + sizeof(rsn->header) + rsn->header.len); + pos += sizeof(rsn->header) + rsn->header.len; + rsn->header.len = cpu_to_le16(rsn->header.len); + } + + /* update curbssparams */ + adapter->curbssparams.channel = + (pbssdesc->phyparamset.dsparamset.currentchan); + + /* Copy the infra. association rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, &rates->rates, + min_t(size_t, sizeof(adapter->curbssparams.datarates), rates->header.len)); + + lbs_pr_debug(1, "ASSOC_CMD: rates->header.len = %d\n", rates->header.len); + + /* set IBSS field */ + if (pbssdesc->mode == IW_MODE_INFRA) { +#define CAPINFO_ESS_MODE 1 + passo->capinfo.ess = CAPINFO_ESS_MODE; + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN); + + /* set the capability info at last */ + memcpy(&tmpcap, &pbssdesc->cap, sizeof(passo->capinfo)); + tmpcap &= CAPINFO_MASK; + lbs_pr_debug(1, "ASSOC_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&passo->capinfo, &tmpcap, sizeof(passo->capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, void *pssid) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads; + int ret = 0; + int cmdappendsize = 0; + int i; + u16 tmpcap; + struct bss_descriptor *pbssdesc; + struct WLAN_802_11_SSID *ssid = pssid; + + ENTER(); + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_start); + + pbssdesc = &adapter->curbssparams.bssdescriptor; + adapter->pattemptedbssdesc = pbssdesc; + + /* + * Fill in the parameters for 2 data structures: + * 1. cmd_ds_802_11_ad_hoc_start command + * 2. adapter->scantable[i] + * + * Driver will fill up SSID, bsstype,IBSS param, Physical Param, + * probe delay, and cap info. + * + * Firmware will fill up beacon period, DTIM, Basic rates + * and operational rates. + */ + + memset(adhs->SSID, 0, IW_ESSID_MAX_SIZE); + + memcpy(adhs->SSID, ssid->ssid, ssid->ssidlength); + + lbs_pr_debug(1, "ADHOC_S_CMD: SSID = %s\n", adhs->SSID); + + memset(pbssdesc->ssid.ssid, 0, IW_ESSID_MAX_SIZE); + memcpy(pbssdesc->ssid.ssid, ssid->ssid, ssid->ssidlength); + + pbssdesc->ssid.ssidlength = ssid->ssidlength; + + /* set the BSS type */ + adhs->bsstype = cmd_bss_type_ibss; + pbssdesc->mode = IW_MODE_ADHOC; + adhs->beaconperiod = adapter->beaconperiod; + + /* set Physical param set */ +#define DS_PARA_IE_ID 3 +#define DS_PARA_IE_LEN 1 + + adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID; + adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN; + + WARN_ON(!adapter->adhocchannel); + + lbs_pr_debug(1, "ADHOC_S_CMD: Creating ADHOC on channel %d\n", + adapter->adhocchannel); + + adapter->curbssparams.channel = adapter->adhocchannel; + + pbssdesc->channel = adapter->adhocchannel; + adhs->phyparamset.dsparamset.currentchan = adapter->adhocchannel; + + memcpy(&pbssdesc->phyparamset, + &adhs->phyparamset, sizeof(union ieeetypes_phyparamset)); + + /* set IBSS param set */ +#define IBSS_PARA_IE_ID 6 +#define IBSS_PARA_IE_LEN 2 + + adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID; + adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN; + adhs->ssparamset.ibssparamset.atimwindow = adapter->atimwindow; + memcpy(&pbssdesc->ssparamset, + &adhs->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + /* set capability info */ + adhs->cap.ess = 0; + adhs->cap.ibss = 1; + pbssdesc->cap.ibss = 1; + + /* probedelay */ + adhs->probedelay = cpu_to_le16(cmd_scan_probe_delay_time); + + /* set up privacy in adapter->scantable[i] */ + if (adapter->secinfo.wep_enabled) { + lbs_pr_debug(1, "ADHOC_S_CMD: WEP enabled, setting privacy on\n"); + pbssdesc->privacy = wlan802_11privfilter8021xWEP; + adhs->cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } else { + lbs_pr_debug(1, "ADHOC_S_CMD: WEP disabled, setting privacy off\n"); + pbssdesc->privacy = wlan802_11privfilteracceptall; + } + + memset(adhs->datarate, 0, sizeof(adhs->datarate)); + + if (adapter->adhoc_grate_enabled) { + memcpy(adhs->datarate, libertas_adhoc_rates_g, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_g))); + } else { + memcpy(adhs->datarate, libertas_adhoc_rates_b, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_b))); + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(adhs->datarate) && adhs->datarate[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* Copy the ad-hoc creating rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, + &adhs->datarate, adapter->curbssparams.numofrates); + + lbs_pr_debug(1, "ADHOC_S_CMD: rates=%02x %02x %02x %02x \n", + adhs->datarate[0], adhs->datarate[1], + adhs->datarate[2], adhs->datarate[3]); + + lbs_pr_debug(1, "ADHOC_S_CMD: AD HOC Start command is ready\n"); + + if (libertas_create_dnld_countryinfo_11d(priv)) { + lbs_pr_debug(1, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &adhs->cap, sizeof(u16)); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&adhs->cap, &tmpcap, sizeof(u16)); + + ret = 0; +done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_stop); + cmd->size = cpu_to_le16(S_DS_GEN); + + return 0; +} + +int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_join *padhocjoin = &cmd->params.adj; + struct bss_descriptor *pbssdesc = pdata_buf; + int cmdappendsize = 0; + int ret = 0; + u8 *card_rates; + int card_rates_size; + u16 tmpcap; + int i; + + ENTER(); + + adapter->pattemptedbssdesc = pbssdesc; + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_join); + + padhocjoin->bssdescriptor.bsstype = cmd_bss_type_ibss; + + padhocjoin->bssdescriptor.beaconperiod = pbssdesc->beaconperiod; + + memcpy(&padhocjoin->bssdescriptor.BSSID, + &pbssdesc->macaddress, ETH_ALEN); + + memcpy(&padhocjoin->bssdescriptor.SSID, + &pbssdesc->ssid.ssid, pbssdesc->ssid.ssidlength); + + memcpy(&padhocjoin->bssdescriptor.phyparamset, + &pbssdesc->phyparamset, sizeof(union ieeetypes_phyparamset)); + + memcpy(&padhocjoin->bssdescriptor.ssparamset, + &pbssdesc->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + memcpy(&tmpcap, &pbssdesc->cap, sizeof(struct ieeetypes_capinfo)); + tmpcap &= CAPINFO_MASK; + + lbs_pr_debug(1, "ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + memcpy(&padhocjoin->bssdescriptor.cap, &tmpcap, + sizeof(struct ieeetypes_capinfo)); + + /* information on BSSID descriptor passed to FW */ + lbs_pr_debug(1, + "ADHOC_J_CMD: BSSID = %2x-%2x-%2x-%2x-%2x-%2x, SSID = %s\n", + padhocjoin->bssdescriptor.BSSID[0], + padhocjoin->bssdescriptor.BSSID[1], + padhocjoin->bssdescriptor.BSSID[2], + padhocjoin->bssdescriptor.BSSID[3], + padhocjoin->bssdescriptor.BSSID[4], + padhocjoin->bssdescriptor.BSSID[5], + padhocjoin->bssdescriptor.SSID); + + /* failtimeout */ + padhocjoin->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); + + /* probedelay */ + padhocjoin->probedelay = + cpu_to_le16(cmd_scan_probe_delay_time); + + /* Copy Data rates from the rates recorded in scan response */ + memset(padhocjoin->bssdescriptor.datarates, 0, + sizeof(padhocjoin->bssdescriptor.datarates)); + memcpy(padhocjoin->bssdescriptor.datarates, pbssdesc->datarates, + min(sizeof(padhocjoin->bssdescriptor.datarates), + sizeof(pbssdesc->datarates))); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + adapter->curbssparams.channel = pbssdesc->channel; + + if (get_common_rates(adapter, padhocjoin->bssdescriptor.datarates, + sizeof(padhocjoin->bssdescriptor.datarates), + card_rates, card_rates_size)) { + lbs_pr_debug(1, "ADHOC_J_CMD: get_common_rates returns error.\n"); + ret = -1; + goto done; + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(padhocjoin->bssdescriptor.datarates) + && padhocjoin->bssdescriptor.datarates[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* + * Copy the adhoc joining rates to Current BSS State structure + */ + memcpy(adapter->curbssparams.datarates, + padhocjoin->bssdescriptor.datarates, + adapter->curbssparams.numofrates); + + padhocjoin->bssdescriptor.ssparamset.ibssparamset.atimwindow = + cpu_to_le16(pbssdesc->atimwindow); + + if (adapter->secinfo.wep_enabled) { + padhocjoin->bssdescriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } + + if (adapter->psmode == wlan802_11powermodemax_psp) { + /* wake up first */ + enum WLAN_802_11_POWER_MODE Localpsmode; + + Localpsmode = wlan802_11powermodecam; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_ps_mode, + cmd_act_set, + 0, 0, &Localpsmode); + + if (ret) { + ret = -1; + goto done; + } + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &padhocjoin->bssdescriptor.cap, + sizeof(struct ieeetypes_capinfo)); + tmpcap = cpu_to_le16(tmpcap); + + memcpy(&padhocjoin->bssdescriptor.cap, + &tmpcap, sizeof(struct ieeetypes_capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + union iwreq_data wrqu; + struct ieeetypes_assocrsp *passocrsp; + struct bss_descriptor *pbssdesc; + + ENTER(); + + passocrsp = (struct ieeetypes_assocrsp *) & resp->params; + + if (passocrsp->statuscode) { + + libertas_mac_event_disconnected(priv); + + lbs_pr_debug(1, + "ASSOC_RESP: Association failed, status code = %d\n", + passocrsp->statuscode); + + ret = -1; + goto done; + } + + lbs_dbg_hex("ASSOC_RESP:", (void *)&resp->params, + le16_to_cpu(resp->size) - S_DS_GEN); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + /* Set the attempted BSSID Index to current */ + pbssdesc = adapter->pattemptedbssdesc; + + lbs_pr_debug(1, "ASSOC_RESP: %s\n", pbssdesc->ssid.ssid); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + /* Set the new BSSID (AP's MAC address) to current BSSID */ + memcpy(adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + + lbs_pr_debug(1, "ASSOC_RESP: currentpacketfilter is %x\n", + adapter->currentpacketfilter); + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = 0; + adapter->NF[TYPE_RXPD][TYPE_AVG] = 0; + + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + lbs_pr_debug(1, "ASSOC_RESP: Associated \n"); + + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} + +int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 command = le16_to_cpu(resp->command); + u16 result = le16_to_cpu(resp->result); + struct cmd_ds_802_11_ad_hoc_result *padhocresult; + union iwreq_data wrqu; + struct bss_descriptor *pbssdesc; + + ENTER(); + + padhocresult = &resp->params.result; + + lbs_pr_debug(1, "ADHOC_S_RESP: size = %d\n", le16_to_cpu(resp->size)); + lbs_pr_debug(1, "ADHOC_S_RESP: command = %x\n", command); + lbs_pr_debug(1, "ADHOC_S_RESP: result = %x\n", result); + + pbssdesc = adapter->pattemptedbssdesc; + + /* + * Join result code 0 --> SUCCESS + */ + if (result) { + lbs_pr_debug(1, "ADHOC_RESP failed\n"); + if (adapter->connect_status == libertas_connected) { + libertas_mac_event_disconnected(priv); + } + + memset(&adapter->curbssparams.bssdescriptor, + 0x00, sizeof(adapter->curbssparams.bssdescriptor)); + + LEAVE(); + return -1; + } + + /* + * Now the join cmd should be successful + * If BSSID has changed use SSID to compare instead of BSSID + */ + lbs_pr_debug(1, "ADHOC_J_RESP %s\n", pbssdesc->ssid.ssid); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + if (command == cmd_ret_802_11_ad_hoc_start) { + /* Update the created network descriptor with the new BSSID */ + memcpy(pbssdesc->macaddress, + padhocresult->BSSID, ETH_ALEN); + } else { + + /* Make a copy of current BSSID descriptor, only needed for join since + * the current descriptor is already being used for adhoc start + */ + memmove(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + } + + /* Set the BSSID from the joined/started descriptor */ + memcpy(&adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + lbs_pr_debug(1, "ADHOC_RESP: - Joined/Started Ad Hoc\n"); + lbs_pr_debug(1, "ADHOC_RESP: channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + padhocresult->BSSID[0], padhocresult->BSSID[1], + padhocresult->BSSID[2], padhocresult->BSSID[3], + padhocresult->BSSID[4], padhocresult->BSSID[5]); + + LEAVE(); + return ret; +} + +int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/join.h b/drivers/net/wireless/libertas/join.h new file mode 100644 index 00000000000..115f5a8ba34 --- /dev/null +++ b/drivers/net/wireless/libertas/join.h @@ -0,0 +1,57 @@ +/** + * Interface for the wlan infrastructure and adhoc join routines + * + * Driver interface functions and type declarations for the join module + * implemented in wlan_join.c. Process all start/join requests for + * both adhoc and infrastructure networks + */ +#ifndef _WLAN_JOIN_H +#define _WLAN_JOIN_H + +#include "defs.h" + +struct cmd_ds_command; +extern int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pssid); +extern int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp); + +extern int libertas_reassociation_thread(void *data); + +struct WLAN_802_11_SSID; +struct bss_descriptor; + +extern int libertas_start_adhoc_network(wlan_private * priv, + struct WLAN_802_11_SSID *adhocssid); +extern int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor *pbssdesc); +extern int libertas_stop_adhoc_network(wlan_private * priv); + +extern int libertas_send_deauthentication(wlan_private * priv); +extern int libertas_send_deauth(wlan_private * priv); + +extern int libertas_do_adhocstop_ioctl(wlan_private * priv); + +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc); + +#endif diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c new file mode 100644 index 00000000000..b9b25ce6591 --- /dev/null +++ b/drivers/net/wireless/libertas/main.c @@ -0,0 +1,1265 @@ +/** + * This file contains the major functions in WLAN + * driver. It includes init, exit, open, close and main + * thread etc.. + */ + +#include <linux/delay.h> +#include <linux/freezer.h> +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "debugfs.h" +#include "assoc.h" + +#define DRIVER_RELEASE_VERSION "320.p0" +const char libertas_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION +#ifdef DEBUG + "-dbg" +#endif + ""; + +#ifdef ENABLE_PM +static struct pm_dev *wlan_pm_dev = NULL; +#endif + +#define WLAN_TX_PWR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_US_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_JP_DEFAULT 16 /*50mW */ +#define WLAN_TX_PWR_FR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_EMEA_DEFAULT 20 /*100mW */ + +/* Format { channel, frequency (MHz), maxtxpower } */ +/* band: 'B/G', region: USA FCC/Canada IC */ +static struct chan_freq_power channel_freq_power_US_BG[] = { + {1, 2412, WLAN_TX_PWR_US_DEFAULT}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT} +}; + +/* band: 'B/G', region: Europe ETSI */ +static struct chan_freq_power channel_freq_power_EU_BG[] = { + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT} +}; + +/* band: 'B/G', region: Spain */ +static struct chan_freq_power channel_freq_power_SPN_BG[] = { + {10, 2457, WLAN_TX_PWR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_DEFAULT} +}; + +/* band: 'B/G', region: France */ +static struct chan_freq_power channel_freq_power_FR_BG[] = { + {10, 2457, WLAN_TX_PWR_FR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_FR_DEFAULT}, + {12, 2467, WLAN_TX_PWR_FR_DEFAULT}, + {13, 2472, WLAN_TX_PWR_FR_DEFAULT} +}; + +/* band: 'B/G', region: Japan */ +static struct chan_freq_power channel_freq_power_JPN_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_DEFAULT}, + {2, 2417, WLAN_TX_PWR_JP_DEFAULT}, + {3, 2422, WLAN_TX_PWR_JP_DEFAULT}, + {4, 2427, WLAN_TX_PWR_JP_DEFAULT}, + {5, 2432, WLAN_TX_PWR_JP_DEFAULT}, + {6, 2437, WLAN_TX_PWR_JP_DEFAULT}, + {7, 2442, WLAN_TX_PWR_JP_DEFAULT}, + {8, 2447, WLAN_TX_PWR_JP_DEFAULT}, + {9, 2452, WLAN_TX_PWR_JP_DEFAULT}, + {10, 2457, WLAN_TX_PWR_JP_DEFAULT}, + {11, 2462, WLAN_TX_PWR_JP_DEFAULT}, + {12, 2467, WLAN_TX_PWR_JP_DEFAULT}, + {13, 2472, WLAN_TX_PWR_JP_DEFAULT}, + {14, 2484, WLAN_TX_PWR_JP_DEFAULT} +}; + +/** + * the structure for channel, frequency and power + */ +struct region_cfp_table { + u8 region; + struct chan_freq_power *cfp_BG; + int cfp_no_BG; +}; + +/** + * the structure for the mapping between region and CFP + */ +static struct region_cfp_table region_cfp_table[] = { + {0x10, /*US FCC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x20, /*CANADA IC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x30, /*EU*/ channel_freq_power_EU_BG, + sizeof(channel_freq_power_EU_BG) / sizeof(struct chan_freq_power), + } + , + {0x31, /*SPAIN*/ channel_freq_power_SPN_BG, + sizeof(channel_freq_power_SPN_BG) / sizeof(struct chan_freq_power), + } + , + {0x32, /*FRANCE*/ channel_freq_power_FR_BG, + sizeof(channel_freq_power_FR_BG) / sizeof(struct chan_freq_power), + } + , + {0x40, /*JAPAN*/ channel_freq_power_JPN_BG, + sizeof(channel_freq_power_JPN_BG) / sizeof(struct chan_freq_power), + } + , +/*Add new region here */ +}; + +/** + * the rates supported by the card + */ +u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES] = + { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 +}; + +/** + * the rates supported + */ +u8 libertas_supported_rates[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc G mode + */ +u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc B mode + */ +u8 libertas_adhoc_rates_b[4] = { 0x82, 0x84, 0x8b, 0x96 }; + +/** + * the global variable of a pointer to wlan_private + * structure variable + */ +static wlan_private *wlanpriv = NULL; + +#define MAX_DEVS 5 +static struct net_device *libertas_devs[MAX_DEVS]; +static int libertas_found = 0; + +/** + * the table to keep region code + */ +u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE] = + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; + +static u8 *default_fw_name = "usb8388.bin"; + +/** + * Attributes exported through sysfs + */ + +/** + * @brief Get function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_get(struct device * dev, + struct device_attribute *attr, char * buf) { + struct cmd_ds_mesh_access mesh_access; + + memset(&mesh_access, 0, sizeof(mesh_access)); + libertas_prepare_and_send_command(to_net_dev(dev)->priv, + cmd_mesh_access, + cmd_act_mesh_get_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + + return snprintf(buf, 3, "%d\n", mesh_access.data[0]); +} + +/** + * @brief Set function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_set(struct device * dev, + struct device_attribute *attr, const char * buf, size_t count) { + struct cmd_ds_mesh_access mesh_access; + + + memset(&mesh_access, 0, sizeof(mesh_access)); + sscanf(buf, "%d", &(mesh_access.data[0])); + libertas_prepare_and_send_command((to_net_dev(dev))->priv, + cmd_mesh_access, + cmd_act_mesh_set_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + return strlen(buf); +} + +/** + * libertas_mpp attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/libertas-mpp) + */ +static DEVICE_ATTR(libertas_mpp, 0644, libertas_mpp_get, + libertas_mpp_set ); + +/** + * @brief Check if the device can be open and wait if necessary. + * + * @param dev A pointer to net_device structure + * @return 0 + * + * For USB adapter, on some systems the device open handler will be + * called before FW ready. Use the following flag check and wait + * function to work around the issue. + * + */ +static int pre_open_check(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + int i = 0; + + while (!adapter->fw_ready && i < 20) { + i++; + msleep_interruptible(100); + } + if (!adapter->fw_ready) { + lbs_pr_info("FW not ready, pre_open_check() return failure\n"); + LEAVE(); + return -1; + } + + return 0; +} + +/** + * @brief This function opens the device + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_dev_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + + priv->open = 1; + + if (adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + } else + netif_carrier_off(priv->wlan_dev.netdev); + + LEAVE(); + return 0; +} +/** + * @brief This function opens the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->mesh_open = 1 ; + netif_start_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +/** + * @brief This function opens the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->infra_open = 1 ; + netif_wake_queue(priv->wlan_dev.netdev); + if (priv->open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +static int wlan_dev_close(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + netif_carrier_off(priv->wlan_dev.netdev); + priv->open = 0; + + LEAVE(); + return 0; +} + +/** + * @brief This function closes the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_close(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) (dev->priv); + + priv->mesh_open = 0; + netif_stop_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + +/** + * @brief This function closes the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_close(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + + netif_stop_queue(priv->wlan_dev.netdev); + priv->infra_open = 0; + if (priv->mesh_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + + +#ifdef ENABLE_PM + +/** + * @brief This function is a callback function. it is called by + * kernel to enter or exit power saving mode. + * + * @param pmdev A pointer to pm_dev + * @param pmreq pm_request_t + * @param pmdata A pointer to pmdata + * @return 0 or -1 + */ +static int wlan_pm_callback(struct pm_dev *pmdev, pm_request_t pmreq, + void *pmdata) +{ + wlan_private *priv = wlanpriv; + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + lbs_pr_debug(1, "WPRM_PM_CALLBACK: pmreq = %d.\n", pmreq); + + switch (pmreq) { + case PM_SUSPEND: + lbs_pr_debug(1, "WPRM_PM_CALLBACK: enter PM_SUSPEND.\n"); + + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + if ((adapter->psstate != PS_STATE_SLEEP) + ) { + lbs_pr_debug(1, + "wlan_pm_callback: can't enter sleep mode\n"); + return -1; + } else { + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_detach(dev); + lbs_pr_debug(1, + "netif_device_detach().\n"); + } + libertas_sbi_suspend(priv); + } + break; + } + + /* in non associated mode */ + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) + netif_device_detach(dev); + + /* + * Storing and restoring of the regs be taken care + * at the driver rest will be done at wlan driver + * this makes driver independent of the card + */ + + libertas_sbi_suspend(priv); + + break; + + case PM_RESUME: + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + { + /* + * Bring the inteface up first + * This case should not happen still ... + */ + libertas_sbi_resume(priv); + + /* + * Attach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_attach(dev); + lbs_pr_debug(1, + "after netif_device_attach().\n"); + } + lbs_pr_debug(1, + "After netif attach, in associated mode.\n"); + } + break; + } + + /* in non associated mode */ + + /* + * Bring the inteface up first + * This case should not happen still ... + */ + + libertas_sbi_resume(priv); + + if (netif_running(dev)) + netif_device_attach(dev); + + lbs_pr_debug(1, "after netif attach, in NON associated mode.\n"); + break; + } + + return 0; +} +#endif /* ENABLE_PM */ + +static int wlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int ret = 0; + wlan_private *priv = dev->priv; + + ENTER(); + + if (priv->wlan_dev.dnld_sent || priv->adapter->TxLockFlag) { + priv->stats.tx_dropped++; + goto done; + } + + netif_stop_queue(priv->wlan_dev.netdev); + + if (libertas_process_tx(priv, skb) == 0) + dev->trans_start = jiffies; +done: + LEAVE(); + return ret; +} + +/** + * @brief Mark mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int mesh_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + wlan_private *priv = dev->priv; + ENTER(); + SET_MESH_FRAME(skb); + LEAVE(); + + return wlan_hard_start_xmit(skb, priv->wlan_dev.netdev); +} + +/** + * @brief Mark non-mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int wlan_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) { + ENTER(); + UNSET_MESH_FRAME(skb); + LEAVE(); + return wlan_hard_start_xmit(skb, dev); +} + +static void wlan_tx_timeout(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + ENTER(); + + lbs_pr_err("tx watch dog timeout!\n"); + + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + dev->trans_start = jiffies; + + if (priv->adapter->currenttxskb) { + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* If we are here, we have not received feedback from + the previous packet. Assume TX_FAIL and move on. */ + priv->adapter->eventcause = 0x01000000; + libertas_send_tx_feedback(priv); + } else + wake_up_interruptible(&priv->mainthread.waitq); + } else if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); + + LEAVE(); +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to wlan_private structure + * @return A pointer to net_device_stats structure + */ +static struct net_device_stats *wlan_get_stats(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + return &priv->stats; +} + +static int wlan_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct sockaddr *phwaddr = addr; + + ENTER(); + + memset(adapter->current_addr, 0, ETH_ALEN); + + /* dev->dev_addr is 8 bytes */ + lbs_dbg_hex("dev->dev_addr:", dev->dev_addr, ETH_ALEN); + + lbs_dbg_hex("addr:", phwaddr->sa_data, ETH_ALEN); + memcpy(adapter->current_addr, phwaddr->sa_data, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_mac_address, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + lbs_pr_debug(1, "set mac address failed.\n"); + ret = -1; + goto done; + } + + lbs_dbg_hex("adapter->macaddr:", adapter->current_addr, ETH_ALEN); + memcpy(dev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(((wlan_private *) dev->priv)->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + +done: + LEAVE(); + return ret; +} + +static int wlan_copy_multicast_address(wlan_adapter * adapter, + struct net_device *dev) +{ + int i = 0; + struct dev_mc_list *mcptr = dev->mc_list; + + for (i = 0; i < dev->mc_count; i++) { + memcpy(&adapter->multicastlist[i], mcptr->dmi_addr, ETH_ALEN); + mcptr = mcptr->next; + } + + return i; + +} + +static void wlan_set_multicast_list(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int oldpacketfilter; + + ENTER(); + + oldpacketfilter = adapter->currentpacketfilter; + + if (dev->flags & IFF_PROMISC) { + lbs_pr_debug(1, "enable Promiscuous mode\n"); + adapter->currentpacketfilter |= + cmd_act_mac_promiscuous_enable; + adapter->currentpacketfilter &= + ~(cmd_act_mac_all_multicast_enable | + cmd_act_mac_multicast_enable); + } else { + /* Multicast */ + adapter->currentpacketfilter &= + ~cmd_act_mac_promiscuous_enable; + + if (dev->flags & IFF_ALLMULTI || dev->mc_count > + MRVDRV_MAX_MULTICAST_LIST_SIZE) { + lbs_pr_debug(1, "Enabling All Multicast!\n"); + adapter->currentpacketfilter |= + cmd_act_mac_all_multicast_enable; + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_all_multicast_enable; + + if (!dev->mc_count) { + lbs_pr_debug(1, "No multicast addresses - " + "disabling multicast!\n"); + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + int i; + + adapter->currentpacketfilter |= + cmd_act_mac_multicast_enable; + + adapter->nr_of_multicastmacaddr = + wlan_copy_multicast_address(adapter, dev); + + lbs_pr_debug(1, "Multicast addresses: %d\n", + dev->mc_count); + + for (i = 0; i < dev->mc_count; i++) { + lbs_pr_debug(1, "Multicast address %d:" + "%x %x %x %x %x %x\n", i, + adapter->multicastlist[i][0], + adapter->multicastlist[i][1], + adapter->multicastlist[i][2], + adapter->multicastlist[i][3], + adapter->multicastlist[i][4], + adapter->multicastlist[i][5]); + } + /* set multicast addresses to firmware */ + libertas_prepare_and_send_command(priv, + cmd_mac_multicast_adr, + cmd_act_set, 0, 0, + NULL); + } + } + } + + if (adapter->currentpacketfilter != oldpacketfilter) { + libertas_set_mac_packet_filter(priv); + } + + LEAVE(); +} + +/** + * @brief This function hanldes the major job in WLAN driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to wlan_thread structure + * @return 0 + */ +static int wlan_service_main_thread(void *data) +{ + struct wlan_thread *thread = data; + wlan_private *priv = thread->priv; + wlan_adapter *adapter = priv->adapter; + wait_queue_t wait; + u8 ireg = 0; + + ENTER(); + + wlan_activate_thread(thread); + + init_waitqueue_entry(&wait, current); + + for (;;) { + lbs_pr_debug(1, "main-thread 111: intcounter=%d " + "currenttxskb=%p dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + add_wait_queue(&thread->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&adapter->driver_lock); + if ((adapter->psstate == PS_STATE_SLEEP) || + (!adapter->intcounter + && (priv->wlan_dev.dnld_sent || adapter->cur_cmd || + list_empty(&adapter->cmdpendingq)))) { + lbs_pr_debug(1, + "main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n", + adapter->connect_status, adapter->intcounter, + adapter->psmode, adapter->psstate); + spin_unlock_irq(&adapter->driver_lock); + schedule(); + } else + spin_unlock_irq(&adapter->driver_lock); + + + lbs_pr_debug(1, + "main-thread 222 (waking up): intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&thread->waitq, &wait); + try_to_freeze(); + + lbs_pr_debug(1, "main-thread 333: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + if (kthread_should_stop() + || adapter->surpriseremoved) { + lbs_pr_debug(1, + "main-thread: break from main thread: surpriseremoved=0x%x\n", + adapter->surpriseremoved); + break; + } + + + spin_lock_irq(&adapter->driver_lock); + if (adapter->intcounter) { + u8 int_status; + adapter->intcounter = 0; + int_status = libertas_sbi_get_int_status(priv, &ireg); + + if (int_status) { + lbs_pr_debug(1, + "main-thread: reading HOST_INT_STATUS_REG failed\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + adapter->hisregcpy |= ireg; + } + + lbs_pr_debug(1, "main-thread 444: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + /* command response? */ + if (adapter->hisregcpy & his_cmdupldrdy) { + lbs_pr_debug(1, "main-thread: cmd response ready.\n"); + + adapter->hisregcpy &= ~his_cmdupldrdy; + spin_unlock_irq(&adapter->driver_lock); + libertas_process_rx_command(priv); + spin_lock_irq(&adapter->driver_lock); + } + + /* Any Card Event */ + if (adapter->hisregcpy & his_cardevent) { + lbs_pr_debug(1, "main-thread: Card Event Activity.\n"); + + adapter->hisregcpy &= ~his_cardevent; + + if (libertas_sbi_read_event_cause(priv)) { + lbs_pr_alert( + "main-thread: libertas_sbi_read_event_cause failed.\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + spin_unlock_irq(&adapter->driver_lock); + libertas_process_event(priv); + } else + spin_unlock_irq(&adapter->driver_lock); + + /* Check if we need to confirm Sleep Request received previously */ + if (adapter->psstate == PS_STATE_PRE_SLEEP) { + if (!priv->wlan_dev.dnld_sent && !adapter->cur_cmd) { + if (adapter->connect_status == + libertas_connected) { + lbs_pr_debug(1, + "main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p " + "dnld_sent=%d cur_cmd=%p, confirm now\n", + adapter->intcounter, + adapter->currenttxskb, + priv->wlan_dev.dnld_sent, + adapter->cur_cmd); + + libertas_ps_confirm_sleep(priv, + (u16) adapter->psmode); + } else { + /* workaround for firmware sending + * deauth/linkloss event immediately + * after sleep request, remove this + * after firmware fixes it + */ + adapter->psstate = PS_STATE_AWAKE; + lbs_pr_alert( + "main-thread: ignore PS_SleepConfirm in non-connected state\n"); + } + } + } + + /* The PS state is changed during processing of Sleep Request + * event above + */ + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) + continue; + + /* Execute the next command */ + if (!priv->wlan_dev.dnld_sent && !priv->adapter->cur_cmd) + libertas_execute_next_command(priv); + + /* Wake-up command waiters which can't sleep in + * libertas_prepare_and_send_command + */ + if (!adapter->nr_cmd_pending) + wake_up_all(&adapter->cmd_pending); + + libertas_tx_runqueue(priv); + } + + del_timer(&adapter->command_timer); + adapter->nr_cmd_pending = 0; + wake_up_all(&adapter->cmd_pending); + wlan_deactivate_thread(thread); + + LEAVE(); + return 0; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the wlan_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to wlan_private structure + */ +wlan_private *wlan_add_card(void *card) +{ + struct net_device *dev = NULL; + struct net_device *mesh_dev = NULL; + wlan_private *priv = NULL; + + ENTER(); + + /* Allocate an Ethernet device and register it */ + if (!(dev = alloc_etherdev(sizeof(wlan_private)))) { + lbs_pr_alert( "Init ethernet device failed!\n"); + return NULL; + } + + priv = dev->priv; + + /* allocate buffer for wlan_adapter */ + if (!(priv->adapter = kmalloc(sizeof(wlan_adapter), GFP_KERNEL))) { + lbs_pr_alert( "Allocate buffer for wlan_adapter failed!\n"); + goto err_kmalloc; + } + + /* Allocate a virtual mesh device */ + if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) { + lbs_pr_debug(1, "Init ethernet device failed!\n"); + return NULL; + } + + /* Both intervaces share the priv structure */ + mesh_dev->priv = priv; + + /* init wlan_adapter */ + memset(priv->adapter, 0, sizeof(wlan_adapter)); + + priv->wlan_dev.netdev = dev; + priv->wlan_dev.card = card; + priv->mesh_open = 0; + priv->infra_open = 0; + priv->mesh_dev = mesh_dev; + wlanpriv = priv; + + SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(mesh_dev); + + /* Setup the OS Interface to our functions */ + dev->open = wlan_open; + dev->hard_start_xmit = wlan_pre_start_xmit; + dev->stop = wlan_close; + dev->do_ioctl = libertas_do_ioctl; + dev->set_mac_address = wlan_set_mac_address; + mesh_dev->open = mesh_open; + mesh_dev->hard_start_xmit = mesh_pre_start_xmit; + mesh_dev->stop = mesh_close; + mesh_dev->do_ioctl = libertas_do_ioctl; + memcpy(mesh_dev->dev_addr, wlanpriv->wlan_dev.netdev->dev_addr, + sizeof(wlanpriv->wlan_dev.netdev->dev_addr)); + +#define WLAN_WATCHDOG_TIMEOUT (5 * HZ) + + dev->tx_timeout = wlan_tx_timeout; + dev->get_stats = wlan_get_stats; + dev->watchdog_timeo = WLAN_WATCHDOG_TIMEOUT; + dev->ethtool_ops = &libertas_ethtool_ops; + mesh_dev->get_stats = wlan_get_stats; + mesh_dev->ethtool_ops = &libertas_ethtool_ops; + +#ifdef WIRELESS_EXT + dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; + mesh_dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; +#endif +#define NETIF_F_DYNALLOC 16 + dev->features |= NETIF_F_DYNALLOC; + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->set_multicast_list = wlan_set_multicast_list; + + INIT_LIST_HEAD(&priv->adapter->cmdfreeq); + INIT_LIST_HEAD(&priv->adapter->cmdpendingq); + + spin_lock_init(&priv->adapter->driver_lock); + init_waitqueue_head(&priv->adapter->cmd_pending); + priv->adapter->nr_cmd_pending = 0; + + lbs_pr_debug(1, "Starting kthread...\n"); + priv->mainthread.priv = priv; + wlan_create_thread(wlan_service_main_thread, + &priv->mainthread, "wlan_main_service"); + + priv->assoc_thread = + create_singlethread_workqueue("libertas_assoc"); + INIT_DELAYED_WORK(&priv->assoc_work, wlan_association_worker); + + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (libertas_sbi_register_dev(priv) < 0) { + lbs_pr_info("failed to register wlan device!\n"); + goto err_registerdev; + } + + /* init FW and HW */ + if (libertas_init_fw(priv)) { + lbs_pr_debug(1, "Firmware Init failed\n"); + goto err_registerdev; + } + + if (register_netdev(dev)) { + lbs_pr_err("Cannot register network device!\n"); + goto err_init_fw; + } + + /* Register virtual mesh interface */ + if (register_netdev(mesh_dev)) { + lbs_pr_info("Cannot register mesh virtual interface!\n"); + goto err_init_fw; + } + + lbs_pr_info("%s: Marvell Wlan 802.11 adapter ", dev->name); + + libertas_debugfs_init_one(priv, dev); + + if (libertas_found == MAX_DEVS) + goto err_init_fw; + libertas_devs[libertas_found] = dev; + libertas_found++; +#ifdef ENABLE_PM + if (!(wlan_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, wlan_pm_callback))) + lbs_pr_alert( "failed to register PM callback\n"); +#endif + if (device_create_file(&(mesh_dev->dev), &dev_attr_libertas_mpp)) + goto err_create_file; + + LEAVE(); + return priv; + +err_create_file: + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); +err_init_fw: + libertas_sbi_unregister_dev(priv); +err_registerdev: + destroy_workqueue(priv->assoc_thread); + /* Stop the thread servicing the interrupts */ + wake_up_interruptible(&priv->mainthread.waitq); + wlan_terminate_thread(&priv->mainthread); + kfree(priv->adapter); +err_kmalloc: + free_netdev(dev); + free_netdev(mesh_dev); + wlanpriv = NULL; + + LEAVE(); + return NULL; +} + +static void wake_pending_cmdnodes(wlan_private *priv) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + list_for_each_entry(cmdnode, &priv->adapter->cmdpendingq, list) { + cmdnode->cmdwaitqwoken = 1; + wake_up_interruptible(&cmdnode->cmdwait_q); + } + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + + +int wlan_remove_card(void *card) +{ + wlan_private *priv = libertas_sbi_get_priv(card); + wlan_adapter *adapter; + struct net_device *dev; + struct net_device *mesh_dev; + union iwreq_data wrqu; + int i; + + ENTER(); + + if (!priv) { + LEAVE(); + return 0; + } + + adapter = priv->adapter; + + if (!adapter) { + LEAVE(); + return 0; + } + + dev = priv->wlan_dev.netdev; + mesh_dev = priv->mesh_dev; + + netif_stop_queue(mesh_dev); + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + wake_pending_cmdnodes(priv); + + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); + unregister_netdev(mesh_dev); + unregister_netdev(dev); + + cancel_delayed_work(&priv->assoc_work); + destroy_workqueue(priv->assoc_thread); + + if (adapter->psmode == wlan802_11powermodemax_psp) { + adapter->psmode = wlan802_11powermodecam; + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + +#ifdef ENABLE_PM + pm_unregister(wlan_pm_dev); +#endif + + adapter->surpriseremoved = 1; + + /* Stop the thread servicing the interrupts */ + wlan_terminate_thread(&priv->mainthread); + + libertas_debugfs_remove_one(priv); + + lbs_pr_debug(1, "Free adapter\n"); + libertas_free_adapter(priv); + + for (i = 0; i<libertas_found; i++) { + if (libertas_devs[i]==priv->wlan_dev.netdev) { + libertas_devs[i] = libertas_devs[--libertas_found]; + libertas_devs[libertas_found] = NULL ; + break ; + } + } + + lbs_pr_debug(1, "Unregister finish\n"); + + priv->wlan_dev.netdev = NULL; + priv->mesh_dev = NULL ; + free_netdev(mesh_dev); + free_netdev(dev); + wlanpriv = NULL; + + LEAVE(); + return 0; +} + +/** + * @brief This function finds the CFP in + * region_cfp_table based on region and band parameter. + * + * @param region The region code + * @param band The band + * @param cfp_no A pointer to CFP number + * @return A pointer to CFP + */ +struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, int *cfp_no) +{ + int i, end; + + ENTER(); + + end = sizeof(region_cfp_table)/sizeof(struct region_cfp_table); + + for (i = 0; i < end ; i++) { + lbs_pr_debug(1, "region_cfp_table[i].region=%d\n", + region_cfp_table[i].region); + if (region_cfp_table[i].region == region) { + *cfp_no = region_cfp_table[i].cfp_no_BG; + LEAVE(); + return region_cfp_table[i].cfp_BG; + } + } + + LEAVE(); + return NULL; +} + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + int i = 0; + + struct chan_freq_power *cfp; + int cfp_no; + + ENTER(); + + memset(adapter->region_channel, 0, sizeof(adapter->region_channel)); + + { + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp != NULL) { + adapter->region_channel[i].nrcfp = cfp_no; + adapter->region_channel[i].CFP = cfp; + } else { + lbs_pr_debug(1, "wrong region code %#x in band B-G\n", + region); + return -1; + } + adapter->region_channel[i].valid = 1; + adapter->region_channel[i].region = region; + adapter->region_channel[i].band = band; + i++; + } + LEAVE(); + return 0; +} + +/** + * @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 dev A pointer to net_device structure + * @return n/a + */ +void libertas_interrupt(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + lbs_pr_debug(1, "libertas_interrupt: intcounter=%d\n", + priv->adapter->intcounter); + + priv->adapter->intcounter++; + + if (priv->adapter->psstate == PS_STATE_SLEEP) { + priv->adapter->psstate = PS_STATE_AWAKE; + netif_wake_queue(dev); + } + + wake_up_interruptible(&priv->mainthread.waitq); + + LEAVE(); +} + +static int wlan_init_module(void) +{ + int ret = 0; + + ENTER(); + + if (libertas_fw_name == NULL) { + libertas_fw_name = default_fw_name; + } + + libertas_debugfs_init(); + + if (libertas_sbi_register()) { + ret = -1; + libertas_debugfs_remove(); + goto done; + } + +done: + LEAVE(); + return ret; +} + +static void wlan_cleanup_module(void) +{ + int i; + + ENTER(); + + for (i = 0; i<libertas_found; i++) { + wlan_private *priv = libertas_devs[i]->priv; + reset_device(priv); + } + + libertas_sbi_unregister(); + libertas_debugfs_remove(); + + LEAVE(); +} + +module_init(wlan_init_module); +module_exit(wlan_cleanup_module); + +MODULE_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h new file mode 100644 index 00000000000..5d118f40cfb --- /dev/null +++ b/drivers/net/wireless/libertas/radiotap.h @@ -0,0 +1,57 @@ +#include <net/ieee80211_radiotap.h> + +struct tx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 rate; + u8 txpower; + u8 rts_retries; + u8 data_retries; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12]; +#endif +} __attribute__ ((packed)); + +#define TX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ + (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ + (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ + 0) + +#define IEEE80211_FC_VERSION_MASK 0x0003 +#define IEEE80211_FC_TYPE_MASK 0x000c +#define IEEE80211_FC_TYPE_MGT 0x0000 +#define IEEE80211_FC_TYPE_CTL 0x0004 +#define IEEE80211_FC_TYPE_DATA 0x0008 +#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 +#define IEEE80211_FC_TOFROMDS_MASK 0x0300 +#define IEEE80211_FC_TODS_MASK 0x0100 +#define IEEE80211_FC_FROMDS_MASK 0x0200 +#define IEEE80211_FC_NODS 0x0000 +#define IEEE80211_FC_TODS 0x0100 +#define IEEE80211_FC_FROMDS 0x0200 +#define IEEE80211_FC_DSTODS 0x0300 + +struct rx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + u16 chan_freq; + u16 chan_flags; + u8 antenna; + u8 antsignal; + u16 rx_flags; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18]; +#endif +} __attribute__ ((packed)); + +#define RX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ + (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \ + 0) + diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c new file mode 100644 index 00000000000..96619a32951 --- /dev/null +++ b/drivers/net/wireless/libertas/rx.c @@ -0,0 +1,445 @@ +/** + * This file contains the handling of RX in wlan driver. + */ +#include <linux/etherdevice.h> +#include <linux/types.h> + +#include "hostcmd.h" +#include "radiotap.h" +#include "decl.h" +#include "dev.h" +#include "wext.h" + +struct eth803hdr { + u8 dest_addr[6]; + u8 src_addr[6]; + u16 h803_len; +} __attribute__ ((packed)); + +struct rfc1042hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + u16 snap_type; +} __attribute__ ((packed)); + +struct rxpackethdr { + struct rxpd rx_pd; + struct eth803hdr eth803_hdr; + struct rfc1042hdr rfc1042_hdr; +} __attribute__ ((packed)); + +struct rx80211packethdr { + struct rxpd rx_pd; + void *eth80211_hdr; +} __attribute__ ((packed)); + +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb); + +/** + * @brief This function computes the avgSNR . + * + * @param priv A pointer to wlan_private structure + * @return avgSNR + */ +static u8 wlan_getavgsnr(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawSNR[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function computes the AvgNF + * + * @param priv A pointer to wlan_private structure + * @return AvgNF + */ +static u8 wlan_getavgnf(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawNF[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function save the raw SNR/NF to our internel buffer + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF < adapter->data_avg_factor) + adapter->numSNRNF++; + adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr; + adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf; + adapter->nextSNRNF++; + if (adapter->nextSNRNF >= adapter->data_avg_factor) + adapter->nextSNRNF = 0; + return; +} + +/** + * @brief This function computes the RSSI in received packet. + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf); + lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr; + adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf; + wlan_save_rawSNRNF(priv, p_rx_pd); + + adapter->rxpd_rate = p_rx_pd->rx_rate; + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE; + adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE; + lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], + adapter->NF[TYPE_RXPD][TYPE_NOAVG]); + + adapter->RSSI[TYPE_RXPD][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + LEAVE(); +} + +void libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb) +{ + lbs_pr_debug(1, "skb->data=%p\n", skb->data); + + if(IS_MESH_FRAME(skb)) + skb->dev = priv->mesh_dev; + else + skb->dev = priv->wlan_dev.netdev; + skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + netif_rx(skb); +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rxpackethdr *p_rx_pkt; + struct rxpd *p_rx_pd; + + int hdrchop; + struct ethhdr *p_ethhdr; + + const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + + ENTER(); + + if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH) + lbs_dbg_hex("RX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + return process_rxed_802_11_packet(priv, skb); + + p_rx_pkt = (struct rxpackethdr *) skb->data; + p_rx_pd = &p_rx_pkt->rx_pd; + if (p_rx_pd->rx_control & RxPD_MESH_FRAME) + SET_MESH_FRAME(skb); + else + UNSET_MESH_FRAME(skb); + + lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) { + lbs_pr_debug(1, "RX error: frame received with bad status\n"); + lbs_pr_alert("rxpd Not OK\n"); + priv->stats.rx_errors++; + ret = 0; + goto done; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %zd = %zd\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_rx_pkt->eth803_hdr.dest_addr)); + lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_rx_pkt->eth803_hdr.src_addr)); + + if (memcmp(&p_rx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type (ethertype) + * + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address right + * before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *) & p_rx_pkt->eth803_hdr + + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) + - sizeof(p_rx_pkt->eth803_hdr.dest_addr) + - sizeof(p_rx_pkt->eth803_hdr.src_addr) + - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); + + memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_ethhdr->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header + * that was removed + */ + hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt; + } else { + lbs_dbg_hex("RX Data: LLC/SNAP", + (u8 *) & p_rx_pkt->rfc1042_hdr, + sizeof(p_rx_pkt->rfc1042_hdr)); + + /* Chop off the rxpd */ + hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt; + } + + /* Chop off the leading header bytes so the skb points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + skb_pull(skb, hdrchop); + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) + adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate); + + wlan_compute_rssi(priv, p_rx_pd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + libertas_upload_rx_packet(priv, skb); + + ret = 0; +done: + LEAVE(); + + return ret; +} + +/** + * @brief This function converts Tx/Rx rates from the Marvell WLAN format + * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u8 convert_mv_rate_to_radiotap(u8 rate) +{ + switch (rate) { + case 0: /* 1 Mbps */ + return 2; + case 1: /* 2 Mbps */ + return 4; + case 2: /* 5.5 Mbps */ + return 11; + case 3: /* 11 Mbps */ + return 22; + case 4: /* 6 Mbps */ + return 12; + case 5: /* 9 Mbps */ + return 18; + case 6: /* 12 Mbps */ + return 24; + case 7: /* 18 Mbps */ + return 36; + case 8: /* 24 Mbps */ + return 48; + case 9: /* 36 Mbps */ + return 72; + case 10: /* 48 Mbps */ + return 96; + case 11: /* 54 Mbps */ + return 108; + } + lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate); + return 0; +} + +/** + * @brief This function processes a received 802.11 packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rx80211packethdr *p_rx_pkt; + struct rxpd *prxpd; + struct rx_radiotap_hdr radiotap_hdr; + struct rx_radiotap_hdr *pradiotap_hdr; + + ENTER(); + + p_rx_pkt = (struct rx80211packethdr *) skb->data; + prxpd = &p_rx_pkt->rx_pd; + + // lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) { + //lbs_pr_debug(1, "RX error: frame received with bad status\n"); + priv->stats.rx_errors++; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %zd = %zd\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + /* create the exported radio header */ + switch (priv->adapter->radiomode) { + case WLAN_RADIOMODE_NONE: + /* no radio header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + + case WLAN_RADIOMODE_RADIOTAP: + /* radiotap header */ + radiotap_hdr.hdr.it_version = 0; + /* XXX must check this value for pad */ + radiotap_hdr.hdr.it_pad = 0; + radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr); + radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT; + /* unknown values */ + radiotap_hdr.flags = 0; + radiotap_hdr.chan_freq = 0; + radiotap_hdr.chan_flags = 0; + radiotap_hdr.antenna = 0; + /* known values */ + radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); + /* XXX must check no carryout */ + radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; + radiotap_hdr.rx_flags = 0; + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) + radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; + //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); + + // lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100)); + + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + + /* add space for the new radio header */ + if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && + pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, + GFP_ATOMIC)) { + lbs_pr_alert( "%s: couldn't pskb_expand_head\n", + __func__); + } + + pradiotap_hdr = + (struct rx_radiotap_hdr *)skb_push(skb, + sizeof(struct + rx_radiotap_hdr)); + memcpy(pradiotap_hdr, &radiotap_hdr, + sizeof(struct rx_radiotap_hdr)); + //lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100)); + break; + + default: + /* unknown header */ + lbs_pr_alert( "Unknown radiomode (%i)\n", + priv->adapter->radiomode); + /* don't export any header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + } + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) { + adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate); + } + + wlan_compute_rssi(priv, prxpd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + libertas_upload_rx_packet(priv, skb); + + ret = 0; +done: + LEAVE(); + + return (ret); +} diff --git a/drivers/net/wireless/libertas/sbi.h b/drivers/net/wireless/libertas/sbi.h new file mode 100644 index 00000000000..59d3a59ccef --- /dev/null +++ b/drivers/net/wireless/libertas/sbi.h @@ -0,0 +1,40 @@ +/** + * This file contains IF layer definitions. + */ + +#ifndef _SBI_H_ +#define _SBI_H_ + +#include <linux/interrupt.h> + +#include "defs.h" + +/** INT status Bit Definition*/ +#define his_cmddnldrdy 0x01 +#define his_cardevent 0x02 +#define his_cmdupldrdy 0x04 + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +#define SBI_EVENT_CAUSE_SHIFT 3 + +/* Probe and Check if the card is present*/ +int libertas_sbi_register_dev(wlan_private * priv); +int libertas_sbi_unregister_dev(wlan_private *); +int libertas_sbi_get_int_status(wlan_private * priv, u8 *); +int libertas_sbi_register(void); +void libertas_sbi_unregister(void); +int libertas_sbi_prog_firmware(wlan_private *); + +int libertas_sbi_read_event_cause(wlan_private *); +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb); +wlan_private *libertas_sbi_get_priv(void *card); + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private *); +int libertas_sbi_resume(wlan_private *); +#endif + +#endif /* _SBI_H */ diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c new file mode 100644 index 00000000000..3c0b1a2a172 --- /dev/null +++ b/drivers/net/wireless/libertas/scan.c @@ -0,0 +1,2005 @@ +/** + * Functions implementing wlan scan IOCTL and firmware command APIs + * + * IOCTL handlers as well as command preperation and response routines + * for sending scan commands to the firmware. + */ +#include <linux/ctype.h> +#include <linux/if.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> + +#include <net/ieee80211.h> +#include <net/iw_handler.h> + +#include "host.h" +#include "decl.h" +#include "dev.h" +#include "scan.h" + +//! Approximate amount of data needed to pass a scan result back to iwlist +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_UINT_LEN \ + + IW_EV_FREQ_LEN \ + + IW_EV_QUAL_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_PARAM_LEN \ + + 40) /* 40 for WPAIE */ + +//! Memory needed to store a max sized channel List TLV for a firmware scan +#define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ + + (MRVDRV_MAX_CHANNELS_PER_SCAN \ + * sizeof(struct chanscanparamset))) + +//! Memory needed to store a max number/size SSID TLV for a firmware scan +#define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) + +//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max +#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ + + sizeof(struct mrvlietypes_numprobes) \ + + CHAN_TLV_MAX_SIZE \ + + SSID_TLV_MAX_SIZE) + +//! The maximum number of channels the firmware can scan per command +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +/** + * @brief Number of channels to scan per firmware scan command issuance. + * + * Number restricted to prevent hitting the limit on the amount of scan data + * returned in a single firmware scan command. + */ +#define MRVDRV_CHANNELS_PER_SCAN_CMD 4 + +//! Scan time specified in the channel TLV for each channel for passive scans +#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100 + +//! Scan time specified in the channel TLV for each channel for active scans +#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 + +//! Macro to enable/disable SSID checking before storing a scan table +#ifdef DISCARD_BAD_SSID +#define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid) +#else +#define CHECK_SSID_IS_VALID(x) 1 +#endif + +/** + * @brief Check if a scanned network compatible with the driver settings + * + * WEP WPA WPA2 ad-hoc encrypt Network + * enabled enabled enabled AES mode privacy WPA WPA2 Compatible + * 0 0 0 0 NONE 0 0 0 yes No security + * 1 0 0 0 NONE 1 0 0 yes Static WEP + * 0 1 0 0 x 1x 1 x yes WPA + * 0 0 1 0 x 1x x 1 yes WPA2 + * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES + * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP + * + * + * @param adapter A pointer to wlan_adapter + * @param index Index in scantable to check against current driver settings + * @param mode Network mode: Infrastructure or IBSS + * + * @return Index in scantable, or error code if negative + */ +static int is_network_compatible(wlan_adapter * adapter, int index, u8 mode) +{ + ENTER(); + + if (adapter->scantable[index].mode == mode) { + if ( !adapter->secinfo.wep_enabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].wpa_ie[0] != WPA_IE + && adapter->scantable[index].rsn_ie[0] != WPA2_IE + && !adapter->scantable[index].privacy) { + /* no security */ + LEAVE(); + return index; + } else if ( adapter->secinfo.wep_enabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].privacy) { + /* static WEP enabled */ + LEAVE(); + return index; + } else if ( !adapter->secinfo.wep_enabled + && adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_ie[0] == WPA_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_ie[0], + adapter->scantable[index].rsn_ie[0], + adapter->secinfo.wep_enabled ? "e" : "d", + adapter->secinfo.WPAenabled ? "e" : "d", + adapter->secinfo.WPA2enabled ? "e" : "d", + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if ( !adapter->secinfo.wep_enabled + && !adapter->secinfo.WPAenabled + && adapter->secinfo.WPA2enabled + && (adapter->scantable[index].rsn_ie[0] == WPA2_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA2 enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA2: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_ie[0], + adapter->scantable[index].rsn_ie[0], + adapter->secinfo.wep_enabled ? "e" : "d", + adapter->secinfo.WPAenabled ? "e" : "d", + adapter->secinfo.WPA2enabled ? "e" : "d", + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if ( !adapter->secinfo.wep_enabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_ie[0] != WPA_IE) + && (adapter->scantable[index].rsn_ie[0] != WPA2_IE) + && adapter->scantable[index].privacy) { + /* dynamic WEP enabled */ + lbs_pr_debug(1, + "is_network_compatible() dynamic WEP: index=%d " + "wpa_ie=%#x wpa2_ie=%#x privacy=%#x\n", + index, + adapter->scantable[index].wpa_ie[0], + adapter->scantable[index].rsn_ie[0], + adapter->scantable[index].privacy); + LEAVE(); + return index; + } + + /* security doesn't match */ + lbs_pr_debug(1, + "is_network_compatible() FAILED: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s privacy=%#x\n", + index, + adapter->scantable[index].wpa_ie[0], + adapter->scantable[index].rsn_ie[0], + adapter->secinfo.wep_enabled ? "e" : "d", + adapter->secinfo.WPAenabled ? "e" : "d", + adapter->secinfo.WPA2enabled ? "e" : "d", + adapter->scantable[index].privacy); + LEAVE(); + return -ECONNREFUSED; + } + + /* mode doesn't match */ + LEAVE(); + return -ENETUNREACH; +} + +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return TRUE or FALSE + */ +static u8 ssid_valid(struct WLAN_802_11_SSID *pssid) +{ + int ssididx; + + for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) { + if (!isprint(pssid->ssid[ssididx])) { + return 0; + } + } + + return 1; +} + +/** + * @brief Post process the scan table after a new scan command has completed + * + * Inspect each entry of the scan table and try to find an entry that + * matches our current associated/joined network from the scan. If + * one is found, update the stored copy of the bssdescriptor for our + * current network. + * + * Debug dump the current scan table contents if compiled accordingly. + * + * @param priv A pointer to wlan_private structure + * + * @return void + */ +static void wlan_scan_process_results(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int foundcurrent; + int i; + + foundcurrent = 0; + + if (adapter->connect_status == libertas_connected) { + /* try to find the current BSSID in the new scan list */ + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, + &adapter->curbssparams.ssid) && + !memcmp(adapter->curbssparams.bssid, + adapter->scantable[i].macaddress, + ETH_ALEN)) { + foundcurrent = 1; + } + } + + if (foundcurrent) { + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + &adapter->scantable[i], + sizeof(adapter->curbssparams.bssdescriptor)); + } + } + + for (i = 0; i < adapter->numinscantable; i++) { + lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " + "RSSI[%03d], SSID[%s]\n", + i, + adapter->scantable[i].macaddress[0], + adapter->scantable[i].macaddress[1], + adapter->scantable[i].macaddress[2], + adapter->scantable[i].macaddress[3], + adapter->scantable[i].macaddress[4], + adapter->scantable[i].macaddress[5], + (s32) adapter->scantable[i].rssi, + adapter->scantable[i].ssid.ssid); + } +} + +/** + * @brief Create a channel list for the driver to scan based on region info + * + * Use the driver region/band information to construct a comprehensive list + * of channels to scan. This routine is used for any scan that is not + * provided a specific channel list to scan. + * + * @param priv A pointer to wlan_private structure + * @param scanchanlist Output parameter: resulting channel list to scan + * @param filteredscan Flag indicating whether or not a BSSID or SSID filter + * is being sent in the command to firmware. Used to + * increase the number of channels sent in a scan + * command and to disable the firmware channel scan + * filter. + * + * @return void + */ +static void wlan_scan_create_channel_list(wlan_private * priv, + struct chanscanparamset * scanchanlist, + u8 filteredscan) +{ + + wlan_adapter *adapter = priv->adapter; + struct region_channel *scanregion; + struct chan_freq_power *cfp; + int rgnidx; + int chanidx; + int nextchan; + u8 scantype; + + chanidx = 0; + + /* Set the default scan type to the user specified type, will later + * be changed to passive on a per channel basis if restricted by + * regulatory requirements (11d or 11h) + */ + scantype = adapter->scantype; + + for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { + if (priv->adapter->enable11d && + adapter->connect_status != libertas_connected) { + /* Scan all the supported chan for the first scan */ + if (!adapter->universal_channel[rgnidx].valid) + continue; + scanregion = &adapter->universal_channel[rgnidx]; + + /* clear the parsed_region_chan for the first scan */ + memset(&adapter->parsed_region_chan, 0x00, + sizeof(adapter->parsed_region_chan)); + } else { + if (!adapter->region_channel[rgnidx].valid) + continue; + scanregion = &adapter->region_channel[rgnidx]; + } + + for (nextchan = 0; + nextchan < scanregion->nrcfp; nextchan++, chanidx++) { + + cfp = scanregion->CFP + nextchan; + + if (priv->adapter->enable11d) { + scantype = + libertas_get_scan_type_11d(cfp->channel, + &adapter-> + parsed_region_chan); + } + + switch (scanregion->band) { + case BAND_B: + case BAND_G: + default: + scanchanlist[chanidx].radiotype = + cmd_scan_radio_type_bg; + break; + } + + if (scantype == cmd_scan_type_passive) { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_PASSIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 1; + } else { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_ACTIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 0; + } + + scanchanlist[chanidx].channumber = cfp->channel; + + if (filteredscan) { + scanchanlist[chanidx].chanscanmode. + disablechanfilt = 1; + } + } + } +} + +/** + * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds + * + * Application layer or other functions can invoke wlan_scan_networks + * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. + * This structure is used as the basis of one or many wlan_scan_cmd_config + * commands that are sent to the command processing module and sent to + * firmware. + * + * Create a wlan_scan_cmd_config based on the following user supplied + * parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - channel list + * + * If the SSID or BSSID filter is not present, disable/clear the filter. + * If the number of probes is not set, use the adapter default setting + * Qualify the channel + * + * @param priv A pointer to wlan_private structure + * @param puserscanin NULL or pointer to scan configuration parameters + * @param ppchantlvout Output parameter: Pointer to the start of the + * channel TLV portion of the output scan config + * @param pscanchanlist Output parameter: Pointer to the resulting channel + * list to scan + * @param pmaxchanperscan Output parameter: Number of channels to scan for + * each issuance of the firmware scan command + * @param pfilteredscan Output parameter: Flag indicating whether or not + * a BSSID or SSID filter is being sent in the + * command to firmware. Used to increase the number + * of channels sent in a scan command and to + * disable the firmware channel scan filter. + * @param pscancurrentonly Output parameter: Flag indicating whether or not + * we are only scanning our current active channel + * + * @return resulting scan configuration + */ +static struct wlan_scan_cmd_config * +wlan_scan_setup_scan_config(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin, + struct mrvlietypes_chanlistparamset ** ppchantlvout, + struct chanscanparamset * pscanchanlist, + int *pmaxchanperscan, + u8 * pfilteredscan, + u8 * pscancurrentonly) +{ + wlan_adapter *adapter = priv->adapter; + const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + struct mrvlietypes_numprobes *pnumprobestlv; + struct mrvlietypes_ssidparamset *pssidtlv; + struct wlan_scan_cmd_config * pscancfgout = NULL; + u8 *ptlvpos; + u16 numprobes; + u16 ssidlen; + int chanidx; + int scantype; + int scandur; + int channel; + int radiotype; + + pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); + if (pscancfgout == NULL) + goto out; + + /* The tlvbufferlen is calculated for each scan command. The TLVs added + * in this routine will be preserved since the routine that sends + * the command will append channelTLVs at *ppchantlvout. The difference + * between the *ppchantlvout and the tlvbuffer start will be used + * to calculate the size of anything we add in this routine. + */ + pscancfgout->tlvbufferlen = 0; + + /* Running tlv pointer. Assigned to ppchantlvout at end of function + * so later routines know where channels can be added to the command buf + */ + ptlvpos = pscancfgout->tlvbuffer; + + /* + * Set the initial scan paramters for progressive scanning. If a specific + * BSSID or SSID is used, the number of channels in the scan command + * will be increased to the absolute maximum + */ + *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; + + /* Initialize the scan as un-filtered by firmware, set to TRUE below if + * a SSID or BSSID filter is sent in the command + */ + *pfilteredscan = 0; + + /* Initialize the scan as not being only on the current channel. If + * the channel list is customized, only contains one channel, and + * is the active channel, this is set true and data flow is not halted. + */ + *pscancurrentonly = 0; + + if (puserscanin) { + + /* Set the bss type scan filter, use adapter setting if unset */ + pscancfgout->bsstype = + (puserscanin->bsstype ? puserscanin->bsstype : adapter-> + scanmode); + + /* Set the number of probes to send, use adapter setting if unset */ + numprobes = (puserscanin->numprobes ? puserscanin->numprobes : + adapter->scanprobes); + + /* + * Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled (all zeros). + */ + memcpy(pscancfgout->specificBSSID, + puserscanin->specificBSSID, + sizeof(pscancfgout->specificBSSID)); + + ssidlen = strlen(puserscanin->specificSSID); + + if (ssidlen) { + pssidtlv = + (struct mrvlietypes_ssidparamset *) pscancfgout-> + tlvbuffer; + pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + pssidtlv->header.len = cpu_to_le16(ssidlen); + memcpy(pssidtlv->ssid, puserscanin->specificSSID, + ssidlen); + ptlvpos += sizeof(pssidtlv->header) + ssidlen; + } + + /* + * The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not truncate + * scan results. That is not an issue with an SSID or BSSID + * filter applied to the scan results in the firmware. + */ + if (ssidlen || (memcmp(pscancfgout->specificBSSID, + &zeromac, sizeof(zeromac)) != 0)) { + *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; + *pfilteredscan = 1; + } + } else { + pscancfgout->bsstype = adapter->scanmode; + numprobes = adapter->scanprobes; + } + + /* If the input config or adapter has the number of Probes set, add tlv */ + if (numprobes) { + pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; + pnumprobestlv->header.type = + cpu_to_le16(TLV_TYPE_NUMPROBES); + pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes); + pnumprobestlv->numprobes = cpu_to_le16(numprobes); + + ptlvpos += + sizeof(pnumprobestlv->header) + pnumprobestlv->header.len; + + pnumprobestlv->header.len = + cpu_to_le16(pnumprobestlv->header.len); + } + + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this fuction (SSID, numprobes). + * channel TLVs will be added past this for each scan command, preserving + * the TLVs that were previously added. + */ + *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; + + if (puserscanin && puserscanin->chanlist[0].channumber) { + + lbs_pr_debug(1, "Scan: Using supplied channel list\n"); + + for (chanidx = 0; + chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX + && puserscanin->chanlist[chanidx].channumber; chanidx++) { + + channel = puserscanin->chanlist[chanidx].channumber; + (pscanchanlist + chanidx)->channumber = channel; + + radiotype = puserscanin->chanlist[chanidx].radiotype; + (pscanchanlist + chanidx)->radiotype = radiotype; + + scantype = puserscanin->chanlist[chanidx].scantype; + + if (scantype == cmd_scan_type_passive) { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 1; + } else { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 0; + } + + if (puserscanin->chanlist[chanidx].scantime) { + scandur = + puserscanin->chanlist[chanidx].scantime; + } else { + if (scantype == cmd_scan_type_passive) { + scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; + } else { + scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; + } + } + + (pscanchanlist + chanidx)->minscantime = + cpu_to_le16(scandur); + (pscanchanlist + chanidx)->maxscantime = + cpu_to_le16(scandur); + } + + /* Check if we are only scanning the current channel */ + if ((chanidx == 1) && (puserscanin->chanlist[0].channumber + == + priv->adapter->curbssparams.channel)) { + *pscancurrentonly = 1; + lbs_pr_debug(1, "Scan: Scanning current channel only"); + } + + } else { + lbs_pr_debug(1, "Scan: Creating full region channel list\n"); + wlan_scan_create_channel_list(priv, pscanchanlist, + *pfilteredscan); + } + +out: + return pscancfgout; +} + +/** + * @brief Construct and send multiple scan config commands to the firmware + * + * Previous routines have created a wlan_scan_cmd_config with any requested + * TLVs. This function splits the channel TLV into maxchanperscan lists + * and sends the portion of the channel TLV along with the other TLVs + * to the wlan_cmd routines for execution in the firmware. + * + * @param priv A pointer to wlan_private structure + * @param maxchanperscan Maximum number channels to be included in each + * scan command sent to firmware + * @param filteredscan Flag indicating whether or not a BSSID or SSID + * filter is being used for the firmware command + * scan command sent to firmware + * @param pscancfgout Scan configuration used for this scan. + * @param pchantlvout Pointer in the pscancfgout where the channel TLV + * should start. This is past any other TLVs that + * must be sent down in each firmware command. + * @param pscanchanlist List of channels to scan in maxchanperscan segments + * + * @return 0 or error return otherwise + */ +static int wlan_scan_channel_list(wlan_private * priv, + int maxchanperscan, + u8 filteredscan, + struct wlan_scan_cmd_config * pscancfgout, + struct mrvlietypes_chanlistparamset * pchantlvout, + struct chanscanparamset * pscanchanlist) +{ + struct chanscanparamset *ptmpchan; + struct chanscanparamset *pstartchan; + u8 scanband; + int doneearly; + int tlvidx; + int ret = 0; + + ENTER(); + + if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { + lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n", + pscancfgout, pchantlvout, pscanchanlist); + return -1; + } + + pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired list */ + ptmpchan = pscanchanlist; + + /* Loop through the desired channel list, sending a new firmware scan + * commands for each maxchanperscan channels (or for 1,6,11 individually + * if configured accordingly) + */ + while (ptmpchan->channumber) { + + tlvidx = 0; + pchantlvout->header.len = 0; + scanband = ptmpchan->radiotype; + pstartchan = ptmpchan; + doneearly = 0; + + /* Construct the channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlvidx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired channel list) + * - doneearly is set (controlling individual scanning of 1,6,11) + */ + while (tlvidx < maxchanperscan && ptmpchan->channumber + && !doneearly) { + + lbs_pr_debug(1, + "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n", + ptmpchan->channumber, ptmpchan->radiotype, + ptmpchan->chanscanmode.passivescan, + ptmpchan->chanscanmode.disablechanfilt, + ptmpchan->maxscantime); + + /* Copy the current channel TLV to the command being prepared */ + memcpy(pchantlvout->chanscanparam + tlvidx, + ptmpchan, sizeof(pchantlvout->chanscanparam)); + + /* Increment the TLV header length by the size appended */ + pchantlvout->header.len += + sizeof(pchantlvout->chanscanparam); + + /* + * The tlv buffer length is set to the number of bytes of the + * between the channel tlv pointer and the start of the + * tlv buffer. This compensates for any TLVs that were appended + * before the channel list. + */ + pscancfgout->tlvbufferlen = ((u8 *) pchantlvout + - pscancfgout->tlvbuffer); + + /* Add the size of the channel tlv header and the data length */ + pscancfgout->tlvbufferlen += + (sizeof(pchantlvout->header) + + pchantlvout->header.len); + + /* Increment the index to the channel tlv we are constructing */ + tlvidx++; + + doneearly = 0; + + /* Stop the loop if the *current* channel is in the 1,6,11 set + * and we are not filtering on a BSSID or SSID. + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + + /* Increment the tmp pointer to the next channel to be scanned */ + ptmpchan++; + + /* Stop the loop if the *next* channel is in the 1,6,11 set. + * This will cause it to be the only channel scanned on the next + * interation + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + } + + /* Send the scan command to the firmware with the specified cfg */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0, + 0, 0, pscancfgout); + } + + LEAVE(); + return ret; +} + +/** + * @brief Internal function used to start a scan based on an input config + * + * Use the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table + * + * @param priv A pointer to wlan_private structure + * @param puserscanin Pointer to the input configuration for the requested + * scan. + * + * @return 0 or < 0 if error + */ +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin) +{ + wlan_adapter *adapter = priv->adapter; + struct mrvlietypes_chanlistparamset *pchantlvout; + struct chanscanparamset * scan_chan_list = NULL; + struct wlan_scan_cmd_config * scan_cfg = NULL; + u8 keeppreviousscan; + u8 filteredscan; + u8 scancurrentchanonly; + int maxchanperscan; + int ret; + + ENTER(); + + scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * + WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); + if (scan_chan_list == NULL) { + ret = -ENOMEM; + goto out; + } + + scan_cfg = wlan_scan_setup_scan_config(priv, + puserscanin, + &pchantlvout, + scan_chan_list, + &maxchanperscan, + &filteredscan, + &scancurrentchanonly); + if (scan_cfg == NULL) { + ret = -ENOMEM; + goto out; + } + + keeppreviousscan = 0; + + if (puserscanin) { + keeppreviousscan = puserscanin->keeppreviousscan; + } + + if (!keeppreviousscan) { + memset(adapter->scantable, 0x00, + sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST); + adapter->numinscantable = 0; + } + + /* Keep the data path active if we are only scanning our current channel */ + if (!scancurrentchanonly) { + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + } + + ret = wlan_scan_channel_list(priv, + maxchanperscan, + filteredscan, + scan_cfg, + pchantlvout, + scan_chan_list); + + /* Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(priv); + + if (priv->adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + } + +out: + if (scan_cfg) + kfree(scan_cfg); + + if (scan_chan_list) + kfree(scan_chan_list); + + LEAVE(); + return ret; +} + +/** + * @brief Inspect the scan response buffer for pointers to expected TLVs + * + * TLVs can be included at the end of the scan response BSS information. + * Parse the data in the buffer for pointers to TLVs that can potentially + * be passed back in the response + * + * @param ptlv Pointer to the start of the TLV buffer to parse + * @param tlvbufsize size of the TLV buffer + * @param ptsftlv Output parameter: Pointer to the TSF TLV if found + * + * @return void + */ +static +void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv, + int tlvbufsize, + struct mrvlietypes_tsftimestamp ** ptsftlv) +{ + struct mrvlietypes_data *pcurrenttlv; + int tlvbufleft; + u16 tlvtype; + u16 tlvlen; + + pcurrenttlv = ptlv; + tlvbufleft = tlvbufsize; + *ptsftlv = NULL; + + lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize); + lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize); + + while (tlvbufleft >= sizeof(struct mrvlietypesheader)) { + tlvtype = le16_to_cpu(pcurrenttlv->header.type); + tlvlen = le16_to_cpu(pcurrenttlv->header.len); + + switch (tlvtype) { + case TLV_TYPE_TSFTIMESTAMP: + *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv; + break; + + default: + lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n", + tlvtype); + /* Give up, this seems corrupted */ + return; + } /* switch */ + + tlvbufleft -= (sizeof(ptlv->header) + tlvlen); + pcurrenttlv = + (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen); + } /* while */ +} + +/** + * @brief Interpret a BSS scan response returned from the firmware + * + * Parse the various fixed fields and IEs passed back for a a BSS probe + * response or beacon from the scan command. Record information as needed + * in the scan table struct bss_descriptor for that entry. + * + * @param pBSSIDEntry Output parameter: Pointer to the BSS Entry + * + * @return 0 or -1 + */ +static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, + u8 ** pbeaconinfo, int *bytesleft) +{ + enum ieeetypes_elementid elemID; + struct ieeetypes_fhparamset *pFH; + struct ieeetypes_dsparamset *pDS; + struct ieeetypes_cfparamset *pCF; + struct ieeetypes_ibssparamset *pibss; + struct ieeetypes_capinfo *pcap; + struct WLAN_802_11_FIXED_IEs fixedie; + u8 *pcurrentptr; + u8 *pRate; + u8 elemlen; + u8 bytestocopy; + u8 ratesize; + u16 beaconsize; + u8 founddatarateie; + int bytesleftforcurrentbeacon; + + struct IE_WPA *pIe; + const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; + + struct ieeetypes_countryinfoset *pcountryinfo; + + ENTER(); + + founddatarateie = 0; + ratesize = 0; + beaconsize = 0; + + if (*bytesleft >= sizeof(beaconsize)) { + /* Extract & convert beacon size from the command buffer */ + memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize)); + beaconsize = le16_to_cpu(beaconsize); + *bytesleft -= sizeof(beaconsize); + *pbeaconinfo += sizeof(beaconsize); + } + + if (beaconsize == 0 || beaconsize > *bytesleft) { + + *pbeaconinfo += *bytesleft; + *bytesleft = 0; + + return -1; + } + + /* Initialize the current working beacon pointer for this BSS iteration */ + pcurrentptr = *pbeaconinfo; + + /* Advance the return beacon pointer past the current beacon */ + *pbeaconinfo += beaconsize; + *bytesleft -= beaconsize; + + bytesleftforcurrentbeacon = beaconsize; + + memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN); + lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n", + pBSSEntry->macaddress[0], pBSSEntry->macaddress[1], + pBSSEntry->macaddress[2], pBSSEntry->macaddress[3], + pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]); + + pcurrentptr += ETH_ALEN; + bytesleftforcurrentbeacon -= ETH_ALEN; + + if (bytesleftforcurrentbeacon < 12) { + lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n"); + return -1; + } + + /* + * next 4 fields are RSSI, time stamp, beacon interval, + * and capability information + */ + + /* RSSI is 1 byte long */ + pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr)); + lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr); + pcurrentptr += 1; + bytesleftforcurrentbeacon -= 1; + + /* time stamp is 8 bytes long */ + memcpy(fixedie.timestamp, pcurrentptr, 8); + memcpy(pBSSEntry->timestamp, pcurrentptr, 8); + pcurrentptr += 8; + bytesleftforcurrentbeacon -= 8; + + /* beacon interval is 2 bytes long */ + memcpy(&fixedie.beaconinterval, pcurrentptr, 2); + pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* capability information is 2 bytes long */ + memcpy(&fixedie.capabilities, pcurrentptr, 2); + lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n", + fixedie.capabilities); + fixedie.capabilities = le16_to_cpu(fixedie.capabilities); + pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities; + memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo)); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* rest of the current buffer are IE's */ + lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n", + bytesleftforcurrentbeacon); + + lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr, + bytesleftforcurrentbeacon); + + if (pcap->privacy) { + lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n"); + pBSSEntry->privacy = wlan802_11privfilter8021xWEP; + } else { + pBSSEntry->privacy = wlan802_11privfilteracceptall; + } + + if (pcap->ibss == 1) { + pBSSEntry->mode = IW_MODE_ADHOC; + } else { + pBSSEntry->mode = IW_MODE_INFRA; + } + + /* process variable IE */ + while (bytesleftforcurrentbeacon >= 2) { + elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr)); + elemlen = *((u8 *) pcurrentptr + 1); + + if (bytesleftforcurrentbeacon < elemlen) { + lbs_pr_debug(1, "InterpretIE: error in processing IE, " + "bytes left < IE length\n"); + bytesleftforcurrentbeacon = 0; + continue; + } + + switch (elemID) { + + case SSID: + pBSSEntry->ssid.ssidlength = elemlen; + memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2), + elemlen); + lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid); + break; + + case SUPPORTED_RATES: + memcpy(pBSSEntry->datarates, (pcurrentptr + 2), + elemlen); + memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2), + elemlen); + ratesize = elemlen; + founddatarateie = 1; + break; + + case EXTRA_IE: + lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n"); + pBSSEntry->extra_ie = 1; + break; + + case FH_PARAM_SET: + pFH = (struct ieeetypes_fhparamset *) pcurrentptr; + memmove(&pBSSEntry->phyparamset.fhparamset, pFH, + sizeof(struct ieeetypes_fhparamset)); + pBSSEntry->phyparamset.fhparamset.dwelltime + = + le16_to_cpu(pBSSEntry->phyparamset.fhparamset. + dwelltime); + break; + + case DS_PARAM_SET: + pDS = (struct ieeetypes_dsparamset *) pcurrentptr; + + pBSSEntry->channel = pDS->currentchan; + + memcpy(&pBSSEntry->phyparamset.dsparamset, pDS, + sizeof(struct ieeetypes_dsparamset)); + break; + + case CF_PARAM_SET: + pCF = (struct ieeetypes_cfparamset *) pcurrentptr; + + memcpy(&pBSSEntry->ssparamset.cfparamset, pCF, + sizeof(struct ieeetypes_cfparamset)); + break; + + case IBSS_PARAM_SET: + pibss = (struct ieeetypes_ibssparamset *) pcurrentptr; + pBSSEntry->atimwindow = + le32_to_cpu(pibss->atimwindow); + + memmove(&pBSSEntry->ssparamset.ibssparamset, pibss, + sizeof(struct ieeetypes_ibssparamset)); + + pBSSEntry->ssparamset.ibssparamset.atimwindow + = + le16_to_cpu(pBSSEntry->ssparamset.ibssparamset. + atimwindow); + break; + + /* Handle Country Info IE */ + case COUNTRY_INFO: + pcountryinfo = + (struct ieeetypes_countryinfoset *) pcurrentptr; + + if (pcountryinfo->len < + sizeof(pcountryinfo->countrycode) + || pcountryinfo->len > 254) { + lbs_pr_debug(1, "InterpretIE: 11D- Err " + "CountryInfo len =%d min=%zd max=254\n", + pcountryinfo->len, + sizeof(pcountryinfo->countrycode)); + LEAVE(); + return -1; + } + + memcpy(&pBSSEntry->countryinfo, + pcountryinfo, pcountryinfo->len + 2); + lbs_dbg_hex("InterpretIE: 11D- CountryInfo:", + (u8 *) pcountryinfo, + (u32) (pcountryinfo->len + 2)); + break; + + case EXTENDED_SUPPORTED_RATES: + /* + * only process extended supported rate + * if data rate is already found. + * data rate IE should come before + * extended supported rate IE + */ + if (founddatarateie) { + if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) { + bytestocopy = + (WLAN_SUPPORTED_RATES - ratesize); + } else { + bytestocopy = elemlen; + } + + pRate = (u8 *) pBSSEntry->datarates; + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + + pRate = (u8 *) pBSSEntry->libertas_supported_rates; + + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + } + break; + + case VENDOR_SPECIFIC_221: +#define IE_ID_LEN_FIELDS_BYTES 2 + pIe = (struct IE_WPA *)pcurrentptr; + + if (memcmp(pIe->oui, oui01, sizeof(oui01))) + break; + + pBSSEntry->wpa_ie_len = min_t(size_t, + elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pBSSEntry->wpa_ie)); + memcpy(pBSSEntry->wpa_ie, pcurrentptr, + pBSSEntry->wpa_ie_len); + lbs_dbg_hex("InterpretIE: Resp WPA_IE", + pBSSEntry->wpa_ie, elemlen); + break; + case WPA2_IE: + pIe = (struct IE_WPA *)pcurrentptr; + + pBSSEntry->rsn_ie_len = min_t(size_t, + elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pBSSEntry->rsn_ie)); + memcpy(pBSSEntry->rsn_ie, pcurrentptr, + pBSSEntry->rsn_ie_len); + lbs_dbg_hex("InterpretIE: Resp WPA2_IE", + pBSSEntry->rsn_ie, elemlen); + break; + case TIM: + break; + + case CHALLENGE_TEXT: + break; + } + + pcurrentptr += elemlen + 2; + + /* need to account for IE ID and IE len */ + bytesleftforcurrentbeacon -= (elemlen + 2); + + } /* while (bytesleftforcurrentbeacon > 2) */ + + return 0; +} + +/** + * @brief Compare two SSIDs + * + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2) +{ + if (!ssid1 || !ssid2) + return -1; + + if (ssid1->ssidlength != ssid2->ssidlength) + return -1; + + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength); +} + +/** + * @brief This function finds a specific compatible BSSID in the scan list + * + * @param adapter A pointer to wlan_adapter + * @param bssid BSSID to find in the scan list + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list, or error return code (< 0) + */ +int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, u8 mode) +{ + int ret = -ENETUNREACH; + int i; + + if (!bssid) + return -EFAULT; + + lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n", + adapter->numinscantable); + + /* Look through the scan table for a compatible match. The ret return + * variable will be equal to the index in the scan table (greater + * than zero) if the network is compatible. The loop will continue + * past a matched bssid that is not compatible in case there is an + * AP with multiple SSIDs assigned to the same BSSID + */ + for (i = 0; ret < 0 && i < adapter->numinscantable; i++) { + if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) { + switch (mode) { + case IW_MODE_INFRA: + case IW_MODE_ADHOC: + ret = is_network_compatible(adapter, i, mode); + break; + default: + ret = i; + break; + } + } + } + + return ret; +} + +/** + * @brief This function finds ssid in ssid list. + * + * @param adapter A pointer to wlan_adapter + * @param ssid SSID to find in the list + * @param bssid BSSID to qualify the SSID selection (if provided) + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list + */ +int libertas_find_SSID_in_list(wlan_adapter * adapter, + struct WLAN_802_11_SSID *ssid, u8 * bssid, u8 mode) +{ + int net = -ENETUNREACH; + u8 bestrssi = 0; + int i; + int j; + + lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) && + (!bssid || + !memcmp(adapter->scantable[i]. + macaddress, bssid, ETH_ALEN))) { + switch (mode) { + case IW_MODE_INFRA: + case IW_MODE_ADHOC: + j = is_network_compatible(adapter, i, mode); + + if (j >= 0) { + if (bssid) { + return i; + } + + if (SCAN_RSSI + (adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter-> + scantable[i]. + rssi); + net = i; + } + } else { + if (net == -ENETUNREACH) { + net = j; + } + } + break; + case IW_MODE_AUTO: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + net = i; + } + break; + } + } + } + + return net; +} + +/** + * @brief This function finds the best SSID in the Scan List + * + * Search the scan table for the best SSID that also matches the current + * adapter network preference (infrastructure or adhoc) + * + * @param adapter A pointer to wlan_adapter + * + * @return index in BSSID list + */ +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, u8 mode) +{ + int bestnet = -ENETUNREACH; + u8 bestrssi = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + switch (mode) { + case IW_MODE_INFRA: + case IW_MODE_ADHOC: + if (is_network_compatible(adapter, i, mode) >= 0) { + if (SCAN_RSSI(adapter->scantable[i].rssi) > + bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + bestnet = i; + } + } + break; + case IW_MODE_AUTO: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i].rssi); + bestnet = i; + } + break; + } + } + + LEAVE(); + return bestnet; +} + +/** + * @brief Find the AP with specific ssid in the scan list + * + * @param priv A pointer to wlan_private structure + * @param pSSID A pointer to AP's ssid + * + * @return 0--success, otherwise--fail + */ +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + u8 preferred_mode, u8 *out_mode) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct bss_descriptor *preqbssid; + int i; + + ENTER(); + + memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID)); + + wlan_scan_networks(priv, NULL); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + i = libertas_find_best_SSID_in_list(adapter, preferred_mode); + if (i < 0) { + ret = -1; + goto out; + } + + preqbssid = &adapter->scantable[i]; + memcpy(pSSID, &preqbssid->ssid, + sizeof(struct WLAN_802_11_SSID)); + *out_mode = preqbssid->mode; + + if (!pSSID->ssidlength) { + ret = -1; + } + +out: + LEAVE(); + return ret; +} + +/** + * @brief Scan Network + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + ENTER(); + + if (!wlan_scan_networks(priv, NULL)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, + NULL); + } + + if (adapter->surpriseremoved) + return -1; + + LEAVE(); + return 0; +} + +/** + * @brief Send a scan command for all available channels filtered on a spec + * + * @param priv A pointer to wlan_private structure + * @param prequestedssid A pointer to AP's ssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (prequestedssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + + memcpy(scancfg.specificSSID, prequestedssid->ssid, + prequestedssid->ssidlength); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief scan an AP with specific BSSID + * + * @param priv A pointer to wlan_private structure + * @param bssid A pointer to AP's bssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan) +{ + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (bssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID)); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (priv->adapter->surpriseremoved) + return -1; + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief Retrieve the scan table entries via wireless tools IOCTL call + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + struct chan_freq_power *cfp; + struct bss_descriptor *pscantable; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + int i; + int j; + int rate; +#define PERFECT_RSSI ((u8)50) +#define WORST_RSSI ((u8)0) +#define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) + u8 rssi; + + u8 buf[16 + 256 * 2]; + u8 *ptr; + + ENTER(); + + /* + * if there's either commands in the queue or one being + * processed return -EAGAIN for iwlist to retry later. + */ + if (adapter->nr_cmd_pending) + return -EAGAIN; + + if (adapter->connect_status == libertas_connected) + lbs_pr_debug(1, "Current ssid: %32s\n", + adapter->curbssparams.ssid.ssid); + + lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n", + adapter->numinscantable); + + /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. + * The new API using SIOCGIWSCAN is only limited by buffer size + * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes + * which is 4096. + */ + for (i = 0; i < adapter->numinscantable; i++) { + if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { + lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p " + "MAX_SCAN_CELL_SIZE=%zd\n", + i, current_ev, end_buf, MAX_SCAN_CELL_SIZE); + break; + } + + pscantable = &adapter->scantable[i]; + + lbs_pr_debug(1, "i=%d ssid: %32s\n", i, pscantable->ssid.ssid); + + cfp = + libertas_find_cfp_by_band_and_channel(adapter, 0, + pscantable->channel); + if (!cfp) { + lbs_pr_debug(1, "Invalid channel number %d\n", + pscantable->channel); + continue; + } + + if (!ssid_valid(&adapter->scantable[i].ssid)) { + continue; + } + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, + &adapter->scantable[i].macaddress, ETH_ALEN); + + iwe.len = IW_EV_ADDR_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //Add the ESSID + iwe.u.data.length = adapter->scantable[i].ssid.ssidlength; + + if (iwe.u.data.length > 32) { + iwe.u.data.length = 32; + } + + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable[i].ssid. + ssid); + + //Add mode + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = adapter->scantable[i].mode; + iwe.len = IW_EV_UINT_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //frequency + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long)cfp->freq * 100000; + iwe.u.freq.e = 1; + iwe.len = IW_EV_FREQ_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; + iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi); + + rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; + iwe.u.qual.qual = + (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * + (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / + (RSSI_DIFF * RSSI_DIFF); + if (iwe.u.qual.qual > 100) + iwe.u.qual.qual = 100; + else if (iwe.u.qual.qual < 1) + iwe.u.qual.qual = 0; + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + iwe.u.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + if ((adapter->mode == IW_MODE_ADHOC) && + !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, + cmd_option_waitforrsp, + 0, NULL); + + if (!ret) { + iwe.u.qual.level = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE); + } + } + iwe.len = IW_EV_QUAL_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (adapter->scantable[i].privacy) { + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + } else { + iwe.u.data.flags = IW_ENCODE_DISABLED; + } + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable->ssid. + ssid); + + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + + /* Bit rate given in 500 kb/s units (+ 0x80) */ + for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates); + j++) { + if (adapter->scantable[i].libertas_supported_rates[j] == 0) { + break; + } + rate = + (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) * + 500000; + if (rate > iwe.u.bitrate.value) { + iwe.u.bitrate.value = rate; + } + + iwe.u.bitrate.value = + (adapter->scantable[i].libertas_supported_rates[j] + & 0x7f) * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, + end_buf, &iwe, iwe.len); + + } + if ((adapter->scantable[i].mode == IW_MODE_ADHOC) + && !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + iwe.u.bitrate.value = 22 * 500000; + } + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, + iwe.len); + + /* Add new value to event */ + current_val = current_ev + IW_EV_LCP_LEN; + + if (adapter->scantable[i].rsn_ie[0] == WPA2_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i].rsn_ie, + adapter->scantable[i].rsn_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i].rsn_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + if (adapter->scantable[i].wpa_ie[0] == WPA_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i].wpa_ie, + adapter->scantable[i].wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i].wpa_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + + + if (adapter->scantable[i].extra_ie != 0) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "extra_ie"); + iwe.u.data.length = strlen(buf); + + lbs_pr_debug(1, "iwe.u.data.length %d\n", + iwe.u.data.length); + lbs_pr_debug(1, "BUF: %s \n", buf); + + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + } + + current_val = current_ev + IW_EV_LCP_LEN; + + /* + * Check if we added any event + */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + + dwrq->length = (current_ev - extra); + dwrq->flags = 0; + + LEAVE(); + return 0; +} + +/** + * @brief Prepare a scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command + * struct to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure to be sent to + * firmware with the cmd_DS_801_11_SCAN structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return 0 or -1 + * + * @sa wlan_scan_create_channel_list + */ +int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; + struct wlan_scan_cmd_config *pscancfg; + + ENTER(); + + pscancfg = pdata_buf; + + /* Set fixed field variables in scan command */ + pscan->bsstype = pscancfg->bsstype; + memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID)); + memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); + + cmd->command = cpu_to_le16(cmd_802_11_scan); + + /* size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16(sizeof(pscan->bsstype) + + sizeof(pscan->BSSID) + + pscancfg->tlvbufferlen + S_DS_GEN); + + lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n", + cmd->command, cmd->size, cmd->seqnum); + LEAVE(); + return 0; +} + +/** + * @brief This function handles the command response of scan + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-----------------------------------------------------------. + * | header (4 * sizeof(u16)): Standard command response hdr | + * .-----------------------------------------------------------. + * | bufsize (u16) : sizeof the BSS Description data | + * .-----------------------------------------------------------. + * | NumOfSet (u8) : Number of BSS Descs returned | + * .-----------------------------------------------------------. + * | BSSDescription data (variable, size given in bufsize) | + * .-----------------------------------------------------------. + * | TLV data (variable, size calculated using header->size, | + * | bufsize and sizeof the fixed fields above) | + * .-----------------------------------------------------------. + * + * @param priv A pointer to wlan_private structure + * @param resp A pointer to cmd_ds_command + * + * @return 0 or -1 + */ +int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_scan_rsp *pscan; + struct bss_descriptor newbssentry; + struct mrvlietypes_data *ptlv; + struct mrvlietypes_tsftimestamp *ptsftlv; + u8 *pbssinfo; + u16 scanrespsize; + int bytesleft; + int numintable; + int bssIdx; + int idx; + int tlvbufsize; + u64 tsfval; + + ENTER(); + + pscan = &resp->params.scanresp; + + if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) { + lbs_pr_debug(1, + "SCAN_RESP: Invalid number of AP returned (%d)!!\n", + pscan->nr_sets); + LEAVE(); + return -1; + } + + bytesleft = le16_to_cpu(pscan->bssdescriptsize); + lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft); + + scanrespsize = le16_to_cpu(resp->size); + lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n", + pscan->nr_sets); + + numintable = adapter->numinscantable; + pbssinfo = pscan->bssdesc_and_tlvbuffer; + + /* The size of the TLV buffer is equal to the entire command response + * size (scanrespsize) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bssdescriptsize as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) + + sizeof(pscan->nr_sets) + + S_DS_GEN); + + ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft); + + /* Search the TLV buffer space in the scan response for any valid TLVs */ + wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv); + + /* + * Process each scan response returned (pscan->nr_sets). Save + * the information in the newbssentry and then insert into the + * driver scan table either as an update to an existing entry + * or as an addition at the end of the table + */ + for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { + /* Zero out the newbssentry we are about to store info in */ + memset(&newbssentry, 0x00, sizeof(newbssentry)); + + /* Process the data fields and IEs returned for this BSS */ + if ((InterpretBSSDescriptionWithIE(&newbssentry, + &pbssinfo, + &bytesleft) == + 0) + && CHECK_SSID_IS_VALID(&newbssentry.ssid)) { + + lbs_pr_debug(1, + "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + newbssentry.macaddress[0], + newbssentry.macaddress[1], + newbssentry.macaddress[2], + newbssentry.macaddress[3], + newbssentry.macaddress[4], + newbssentry.macaddress[5]); + + /* + * Search the scan table for the same bssid + */ + for (bssIdx = 0; bssIdx < numintable; bssIdx++) { + if (memcmp(newbssentry.macaddress, + adapter->scantable[bssIdx]. + macaddress, + sizeof(newbssentry.macaddress)) == + 0) { + /* + * If the SSID matches as well, it is a duplicate of + * this entry. Keep the bssIdx set to this + * entry so we replace the old contents in the table + */ + if ((newbssentry.ssid.ssidlength == + adapter->scantable[bssIdx].ssid. + ssidlength) + && + (memcmp + (newbssentry.ssid.ssid, + adapter->scantable[bssIdx].ssid. + ssid, + newbssentry.ssid.ssidlength) == + 0)) { + lbs_pr_debug(1, + "SCAN_RESP: Duplicate of index: %d\n", + bssIdx); + break; + } + } + } + /* + * If the bssIdx is equal to the number of entries in the table, + * the new entry was not a duplicate; append it to the scan + * table + */ + if (bssIdx == numintable) { + /* Range check the bssIdx, keep it limited to the last entry */ + if (bssIdx == MRVDRV_MAX_BSSID_LIST) { + bssIdx--; + } else { + numintable++; + } + } + + /* + * If the TSF TLV was appended to the scan results, save the + * this entries TSF value in the networktsf field. The + * networktsf is the firmware's TSF value at the time the + * beacon or probe response was received. + */ + if (ptsftlv) { + memcpy(&tsfval, &ptsftlv->tsftable[idx], + sizeof(tsfval)); + tsfval = le64_to_cpu(tsfval); + + memcpy(&newbssentry.networktsf, + &tsfval, sizeof(newbssentry.networktsf)); + } + + /* Copy the locally created newbssentry to the scan table */ + memcpy(&adapter->scantable[bssIdx], + &newbssentry, + sizeof(adapter->scantable[bssIdx])); + + } else { + + /* error parsing/interpreting the scan response, skipped */ + lbs_pr_debug(1, "SCAN_RESP: " + "InterpretBSSDescriptionWithIE returned ERROR\n"); + } + } + + lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", + pscan->nr_sets, numintable - adapter->numinscantable, + numintable); + + /* Update the total number of BSSIDs in the scan table */ + adapter->numinscantable = numintable; + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h new file mode 100644 index 00000000000..405f4f0fe57 --- /dev/null +++ b/drivers/net/wireless/libertas/scan.h @@ -0,0 +1,214 @@ +/** + * Interface for the wlan network scan routines + * + * Driver interface functions and type declarations for the scan module + * implemented in wlan_scan.c. + */ +#ifndef _WLAN_SCAN_H +#define _WLAN_SCAN_H + +#include <net/ieee80211.h> +#include "hostcmd.h" + +/** + * @brief Maximum number of channels that can be sent in a setuserscan ioctl + * + * @sa wlan_ioctl_user_scan_cfg + */ +#define WLAN_IOCTL_USER_SCAN_CHAN_MAX 50 + +//! Infrastructure BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_BSS 1 + +//! Adhoc BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_IBSS 2 + +//! Adhoc or Infrastructure BSS scan type in wlan_scan_cmd_config, no filter +#define WLAN_SCAN_BSS_TYPE_ANY 3 + +/** + * @brief Structure used internally in the wlan driver to configure a scan. + * + * Sent to the command processing module to configure the firmware + * scan command prepared by libertas_cmd_80211_scan. + * + * @sa wlan_scan_networks + * + */ +struct wlan_scan_cmd_config { + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Specific BSSID used to filter scan results in the firmware + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief length of TLVs sent in command starting at tlvBuffer + */ + int tlvbufferlen; + + /** + * @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t + * @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t + */ + u8 tlvbuffer[1]; //!< SSID TLV(s) and ChanList TLVs are stored here +}; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +struct wlan_ioctl_user_scan_chan { + u8 channumber; //!< channel Number to scan + u8 radiotype; //!< Radio type: 'B/G' band = 0, 'A' band = 1 + u8 scantype; //!< Scan type: Active = 0, Passive = 1 + u16 scantime; //!< Scan duration in milliseconds; if 0 default used +}; + +/** + * @brief IOCTL input structure to configure an immediate scan cmd to firmware + * + * Used in the setuserscan (WLAN_SET_USER_SCAN) private ioctl. Specifies + * a number of parameters to be used in general for the scan as well + * as a channel list (wlan_ioctl_user_scan_chan) for each scan period + * desired. + * + * @sa libertas_set_user_scan_ioctl + */ +struct wlan_ioctl_user_scan_cfg { + + /** + * @brief Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + u8 keeppreviousscan; //!< Do not erase the existing scan results + + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Configure the number of probe requests for active chan scans + */ + u8 numprobes; + + /** + * @brief BSSID filter sent in the firmware command to limit the results + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief SSID filter sent in the firmware command to limit the results + */ + char specificSSID[IW_ESSID_MAX_SIZE + 1]; + + /** + * @brief Variable number (fixed maximum) of channels to scan up + */ + struct wlan_ioctl_user_scan_chan chanlist[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; +}; + +/** + * @brief Structure used to store information for each beacon/probe response + */ +struct bss_descriptor { + u8 macaddress[ETH_ALEN]; + + struct WLAN_802_11_SSID ssid; + + /* WEP encryption requirement */ + u32 privacy; + + /* receive signal strength in dBm */ + long rssi; + + u32 channel; + + u16 beaconperiod; + + u32 atimwindow; + + u8 mode; + u8 libertas_supported_rates[WLAN_SUPPORTED_RATES]; + + int extra_ie; + + u8 timestamp[8]; //!< TSF value included in the beacon/probe response + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[WLAN_SUPPORTED_RATES]; + + __le64 networktsf; //!< TSF timestamp from the current firmware TSF + + struct ieeetypes_countryinfofullset countryinfo; + + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; +}; + +extern int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, + struct WLAN_802_11_SSID *ssid2); +extern int libertas_find_SSID_in_list(wlan_adapter * adapter, struct WLAN_802_11_SSID *ssid, + u8 * bssid, u8 mode); +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, u8 mode); +extern int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, u8 mode); + +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + u8 preferred_mode, u8 *out_mode); + +extern int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan); +extern int libertas_send_specific_BSSID_scan(wlan_private * priv, + u8 * bssid, u8 keeppreviousscan); + +extern int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_scan(wlan_private * priv, + struct cmd_ds_command *resp); + +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin); + +struct ifreq; + +struct iw_point; +struct iw_param; +struct iw_request_info; +extern int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra); +extern int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra); + +#endif /* _WLAN_SCAN_H */ diff --git a/drivers/net/wireless/libertas/thread.h b/drivers/net/wireless/libertas/thread.h new file mode 100644 index 00000000000..207b8a6cc33 --- /dev/null +++ b/drivers/net/wireless/libertas/thread.h @@ -0,0 +1,52 @@ +#ifndef __WLAN_THREAD_H_ +#define __WLAN_THREAD_H_ + +#include <linux/kthread.h> + +struct wlan_thread { + struct task_struct *task; + wait_queue_head_t waitq; + pid_t pid; + void *priv; +}; + +static inline void wlan_activate_thread(struct wlan_thread * thr) +{ + /** Record the thread pid */ + thr->pid = current->pid; + + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitq); +} + +static inline void wlan_deactivate_thread(struct wlan_thread * thr) +{ + ENTER(); + + thr->pid = 0; + + LEAVE(); +} + +static inline void wlan_create_thread(int (*wlanfunc) (void *), + struct wlan_thread * thr, char *name) +{ + thr->task = kthread_run(wlanfunc, thr, "%s", name); +} + +static inline int wlan_terminate_thread(struct wlan_thread * thr) +{ + ENTER(); + + /* Check if the thread is active or not */ + if (!thr->pid) { + printk(KERN_ERR "Thread does not exist\n"); + return -1; + } + kthread_stop(thr->task); + + LEAVE(); + return 0; +} + +#endif diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c new file mode 100644 index 00000000000..d4b13478c9a --- /dev/null +++ b/drivers/net/wireless/libertas/tx.c @@ -0,0 +1,285 @@ +/** + * This file contains the handling of TX in wlan driver. + */ +#include <linux/netdevice.h> + +#include "hostcmd.h" +#include "radiotap.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "wext.h" + +/** + * @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE + * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u32 convert_radiotap_rate_to_mv(u8 rate) +{ + switch (rate) { + case 2: /* 1 Mbps */ + return 0 | (1 << 4); + case 4: /* 2 Mbps */ + return 1 | (1 << 4); + case 11: /* 5.5 Mbps */ + return 2 | (1 << 4); + case 22: /* 11 Mbps */ + return 3 | (1 << 4); + case 12: /* 6 Mbps */ + return 4 | (1 << 4); + case 18: /* 9 Mbps */ + return 5 | (1 << 4); + case 24: /* 12 Mbps */ + return 6 | (1 << 4); + case 36: /* 18 Mbps */ + return 7 | (1 << 4); + case 48: /* 24 Mbps */ + return 8 | (1 << 4); + case 72: /* 36 Mbps */ + return 9 | (1 << 4); + case 96: /* 48 Mbps */ + return 10 | (1 << 4); + case 108: /* 54 Mbps */ + return 11 | (1 << 4); + } + return 0; +} + +/** + * @brief This function processes a single packet and sends + * to IF layer + * + * @param priv A pointer to wlan_private structure + * @param skb A pointer to skb which includes TX packet + * @return 0 or -1 + */ +static int SendSinglePacket(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct txpd localtxpd; + struct txpd *plocaltxpd = &localtxpd; + u8 *p802x_hdr; + struct tx_radiotap_hdr *pradiotap_hdr; + u32 new_rate; + u8 *ptr = priv->adapter->tmptxbuf; + + ENTER(); + + if (priv->adapter->surpriseremoved) + return -1; + + if ((priv->adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { + lbs_pr_debug(1, "Tx error: Bad skb length %d : %zd\n", + skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); + ret = -1; + goto done; + } + + memset(plocaltxpd, 0, sizeof(struct txpd)); + + plocaltxpd->tx_packet_length = skb->len; + + /* offset of actual data */ + plocaltxpd->tx_packet_location = sizeof(struct txpd); + + /* TxCtrl set by user or default */ + plocaltxpd->tx_control = adapter->pkttxctrl; + + p802x_hdr = skb->data; + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + + /* locate radiotap header */ + pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data; + + /* set txpd fields from the radiotap header */ + new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate); + if (new_rate != 0) { + /* erase tx_control[4:0] */ + plocaltxpd->tx_control &= ~0x1f; + /* write new tx_control[4:0] */ + plocaltxpd->tx_control |= new_rate; + } + + /* skip the radiotap header */ + p802x_hdr += sizeof(struct tx_radiotap_hdr); + plocaltxpd->tx_packet_length -= sizeof(struct tx_radiotap_hdr); + + } + /* copy destination address from 802.3 or 802.11 header */ + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); + else + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); + + lbs_dbg_hex("txpd", (u8 *) plocaltxpd, sizeof(struct txpd)); + + if (IS_MESH_FRAME(skb)) { + plocaltxpd->tx_control |= TxPD_MESH_FRAME; + } + + memcpy(ptr, plocaltxpd, sizeof(struct txpd)); + + ptr += sizeof(struct txpd); + + lbs_dbg_hex("Tx Data", (u8 *) p802x_hdr, plocaltxpd->tx_packet_length); + memcpy(ptr, p802x_hdr, plocaltxpd->tx_packet_length); + ret = libertas_sbi_host_to_card(priv, MVMS_DAT, + priv->adapter->tmptxbuf, + plocaltxpd->tx_packet_length + + sizeof(struct txpd)); + + if (ret) { + lbs_pr_debug(1, "Tx error: libertas_sbi_host_to_card failed: 0x%X\n", ret); + goto done; + } + + lbs_pr_debug(1, "SendSinglePacket succeeds\n"); + + done: + if (!ret) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } else { + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + } + + if (!ret && priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* Keep the skb to echo it back once Tx feedback is + received from FW */ + skb_orphan(skb); + /* stop processing outgoing pkts */ + netif_stop_queue(priv->wlan_dev.netdev); + /* freeze any packets already in our queues */ + priv->adapter->TxLockFlag = 1; + } else { + dev_kfree_skb_any(skb); + priv->adapter->currenttxskb = NULL; + } + + LEAVE(); + return ret; +} + + +void libertas_tx_runqueue(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + spin_lock(&adapter->txqueue_lock); + for (i = 0; i < adapter->tx_queue_idx; i++) { + struct sk_buff *skb = adapter->tx_queue_ps[i]; + spin_unlock(&adapter->txqueue_lock); + SendSinglePacket(priv, skb); + spin_lock(&adapter->txqueue_lock); + } + adapter->tx_queue_idx = 0; + spin_unlock(&adapter->txqueue_lock); +} + +static void wlan_tx_queue(wlan_private *priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + + spin_lock(&adapter->txqueue_lock); + + WARN_ON(priv->adapter->tx_queue_idx >= NR_TX_QUEUE); + adapter->tx_queue_ps[adapter->tx_queue_idx++] = skb; + if (adapter->tx_queue_idx == NR_TX_QUEUE) + netif_stop_queue(priv->wlan_dev.netdev); + else + netif_start_queue(priv->wlan_dev.netdev); + + spin_unlock(&adapter->txqueue_lock); +} + +/** + * @brief This function checks the conditions and sends packet to IF + * layer if everything is ok. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb) +{ + int ret = -1; + + ENTER(); + + lbs_dbg_hex("TX Data", skb->data, min_t(unsigned int, skb->len, 100)); + + if (priv->wlan_dev.dnld_sent) { + lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n", + priv->wlan_dev.dnld_sent); + goto done; + } + + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) { + wlan_tx_queue(priv, skb); + return ret; + } + + priv->adapter->currenttxskb = skb; + + ret = SendSinglePacket(priv, skb); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends to the host the last transmitted packet, + * filling the radiotap headers with transmission information. + * + * @param priv A pointer to wlan_private structure + * @param status A 32 bit value containing transmission status. + * + * @returns void + */ +void libertas_send_tx_feedback(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct tx_radiotap_hdr *radiotap_hdr; + u32 status = adapter->eventcause; + int txfail; + int try_count; + + if (adapter->radiomode != WLAN_RADIOMODE_RADIOTAP || + adapter->currenttxskb == NULL) + return; + + radiotap_hdr = (struct tx_radiotap_hdr *)adapter->currenttxskb->data; + + if ((adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX feedback: ", (u8 *) radiotap_hdr, + min_t(unsigned int, adapter->currenttxskb->len, 100)); + + txfail = (status >> 24); + +#if 0 + /* The version of roofnet that we've tested does not use this yet + * But it may be used in the future. + */ + if (txfail) + radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL; +#endif + try_count = (status >> 16) & 0xff; + radiotap_hdr->data_retries = (try_count) ? + (1 + adapter->txretrycount - try_count) : 0; + libertas_upload_rx_packet(priv, adapter->currenttxskb); + adapter->currenttxskb = NULL; + priv->adapter->TxLockFlag = 0; + if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); +} diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h new file mode 100644 index 00000000000..09d62f8b1a1 --- /dev/null +++ b/drivers/net/wireless/libertas/types.h @@ -0,0 +1,289 @@ +/** + * This header file contains definition for global types + */ +#ifndef _WLAN_TYPES_ +#define _WLAN_TYPES_ + +#include <linux/if_ether.h> + +/** IEEE type definitions */ +enum ieeetypes_elementid { + SSID = 0, + SUPPORTED_RATES, + FH_PARAM_SET, + DS_PARAM_SET, + CF_PARAM_SET, + TIM, + IBSS_PARAM_SET, + COUNTRY_INFO = 7, + + CHALLENGE_TEXT = 16, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + + WPA_IE = 221, + WPA2_IE = 48, + + EXTRA_IE = 133, +} __attribute__ ((packed)); + +#define CAPINFO_MASK (~(0xda00)) + +struct ieeetypes_capinfo { + u8 ess:1; + u8 ibss:1; + u8 cfpollable:1; + u8 cfpollrqst:1; + u8 privacy:1; + u8 shortpreamble:1; + u8 pbcc:1; + u8 chanagility:1; + u8 spectrummgmt:1; + u8 rsrvd3:1; + u8 shortslottime:1; + u8 apsd:1; + u8 rsvrd2:1; + u8 dsssofdm:1; + u8 rsrvd1:2; +} __attribute__ ((packed)); + +struct ieeetypes_cfparamset { + u8 elementid; + u8 len; + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + + +struct ieeetypes_ibssparamset { + u8 elementid; + u8 len; + u16 atimwindow; +} __attribute__ ((packed)); + +union IEEEtypes_ssparamset { + struct ieeetypes_cfparamset cfparamset; + struct ieeetypes_ibssparamset ibssparamset; +} __attribute__ ((packed)); + +struct ieeetypes_fhparamset { + u8 elementid; + u8 len; + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct ieeetypes_dsparamset { + u8 elementid; + u8 len; + u8 currentchan; +} __attribute__ ((packed)); + +union ieeetypes_phyparamset { + struct ieeetypes_fhparamset fhparamset; + struct ieeetypes_dsparamset dsparamset; +} __attribute__ ((packed)); + +struct ieeetypes_assocrsp { + struct ieeetypes_capinfo capability; + u16 statuscode; + u16 aid; + u8 iebuffer[1]; +} __attribute__ ((packed)); + +/** TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/* Terminating TLV type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +#define TLV_TYPE_SSID 0x0000 +#define TLV_TYPE_RATES 0x0001 +#define TLV_TYPE_PHY_FH 0x0002 +#define TLV_TYPE_PHY_DS 0x0003 +#define TLV_TYPE_CF 0x0004 +#define TLV_TYPE_IBSS 0x0006 + +#define TLV_TYPE_DOMAIN 0x0007 + +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) +#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) +#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) +#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) +#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) + +/** TLV related data structures*/ +struct mrvlietypesheader { + u16 type; + u16 len; +} __attribute__ ((packed)); + +struct mrvlietypes_data { + struct mrvlietypesheader header; + u8 Data[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ratesparamset { + struct mrvlietypesheader header; + u8 rates[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ssidparamset { + struct mrvlietypesheader header; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_wildcardssidparamset { + struct mrvlietypesheader header; + u8 MaxSsidlength; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct chanscanmode { + u8 passivescan:1; + u8 disablechanfilt:1; + u8 reserved_2_7:6; +} __attribute__ ((packed)); + +struct chanscanparamset { + u8 radiotype; + u8 channumber; + struct chanscanmode chanscanmode; + u16 minscantime; + u16 maxscantime; +} __attribute__ ((packed)); + +struct mrvlietypes_chanlistparamset { + struct mrvlietypesheader header; + struct chanscanparamset chanscanparam[1]; +} __attribute__ ((packed)); + +struct cfparamset { + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + +struct ibssparamset { + u16 atimwindow; +} __attribute__ ((packed)); + +struct mrvlietypes_ssparamset { + struct mrvlietypesheader header; + union { + struct cfparamset cfparamset[1]; + struct ibssparamset ibssparamset[1]; + } cf_ibss; +} __attribute__ ((packed)); + +struct fhparamset { + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct dsparamset { + u8 currentchan; +} __attribute__ ((packed)); + +struct mrvlietypes_phyparamset { + struct mrvlietypesheader header; + union { + struct fhparamset fhparamset[1]; + struct dsparamset dsparamset[1]; + } fh_ds; +} __attribute__ ((packed)); + +struct mrvlietypes_rsnparamset { + struct mrvlietypesheader header; + u8 rsnie[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_tsftimestamp { + struct mrvlietypesheader header; + __le64 tsftable[1]; +} __attribute__ ((packed)); + +/** Local Power capability */ +struct mrvlietypes_powercapability { + struct mrvlietypesheader header; + s8 minpower; + s8 maxpower; +} __attribute__ ((packed)); + +struct mrvlietypes_rssithreshold { + struct mrvlietypesheader header; + u8 rssivalue; + u8 rssifreq; +} __attribute__ ((packed)); + +struct mrvlietypes_snrthreshold { + struct mrvlietypesheader header; + u8 snrvalue; + u8 snrfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_failurecount { + struct mrvlietypesheader header; + u8 failvalue; + u8 Failfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_beaconsmissed { + struct mrvlietypesheader header; + u8 beaconmissed; + u8 reserved; +} __attribute__ ((packed)); + +struct mrvlietypes_numprobes { + struct mrvlietypesheader header; + u16 numprobes; +} __attribute__ ((packed)); + +struct mrvlietypes_bcastprobe { + struct mrvlietypesheader header; + u16 bcastprobe; +} __attribute__ ((packed)); + +struct mrvlietypes_numssidprobe { + struct mrvlietypesheader header; + u16 numssidprobe; +} __attribute__ ((packed)); + +struct led_pin { + u8 led; + u8 pin; +} __attribute__ ((packed)); + +struct mrvlietypes_ledgpio { + struct mrvlietypesheader header; + struct led_pin ledpin[1]; +} __attribute__ ((packed)); + +#endif /* _WLAN_TYPES_ */ diff --git a/drivers/net/wireless/libertas/version.h b/drivers/net/wireless/libertas/version.h new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/drivers/net/wireless/libertas/version.h @@ -0,0 +1 @@ + diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c new file mode 100644 index 00000000000..69f52b6e59c --- /dev/null +++ b/drivers/net/wireless/libertas/wext.c @@ -0,0 +1,2423 @@ +/** + * This file contains ioctl functions + */ +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> +#include <linux/bitops.h> + +#include <net/ieee80211.h> +#include <net/iw_handler.h> + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" +#include "assoc.h" + + +/** + * @brief Convert mw value to dbm value + * + * @param mw the value of mw + * @return the value of dbm + */ +static int mw_to_dbm(int mw) +{ + if (mw < 2) + return 0; + else if (mw < 3) + return 3; + else if (mw < 4) + return 5; + else if (mw < 6) + return 7; + else if (mw < 7) + return 8; + else if (mw < 8) + return 9; + else if (mw < 10) + return 10; + else if (mw < 13) + return 11; + else if (mw < 16) + return 12; + else if (mw < 20) + return 13; + else if (mw < 25) + return 14; + else if (mw < 32) + return 15; + else if (mw < 40) + return 16; + else if (mw < 50) + return 17; + else if (mw < 63) + return 18; + else if (mw < 79) + return 19; + else if (mw < 100) + return 20; + else + return 21; +} + +/** + * @brief Find the channel frequency power info with specific channel + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param channel the channel for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +struct chan_freq_power *libertas_find_cfp_by_band_and_channel(wlan_adapter * adapter, + u8 band, u16 channel) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].channel == channel) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && channel) + lbs_pr_debug(1, "libertas_find_cfp_by_band_and_channel(): cannot find " + "cfp by band %d & channel %d\n", band, channel); + + return cfp; +} + +/** + * @brief Find the channel frequency power info with specific frequency + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param freq the frequency for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +static struct chan_freq_power *find_cfp_by_band_and_freq(wlan_adapter * adapter, + u8 band, u32 freq) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].freq == freq) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && freq) + lbs_pr_debug(1, "find_cfp_by_band_and_freql(): cannot find cfp by " + "band %d & freq %d\n", band, freq); + + return cfp; +} + +static int updatecurrentchannel(wlan_private * priv) +{ + int ret; + + /* + ** the channel in f/w could be out of sync, get the current channel + */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_get, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "Current channel = %d\n", + priv->adapter->curbssparams.channel); + + return ret; +} + +static int setcurrentchannel(wlan_private * priv, int channel) +{ + lbs_pr_debug(1, "Set channel = %d\n", channel); + + /* + ** Current channel is not set to adhocchannel requested, set channel + */ + return (libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_set, + cmd_option_waitforrsp, 0, &channel)); +} + +static int changeadhocchannel(wlan_private * priv, int channel) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + adapter->adhocchannel = channel; + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel == adapter->adhocchannel) { + /* adhocchannel is set to the current channel already */ + LEAVE(); + return 0; + } + + lbs_pr_debug(1, "Updating channel from %d to %d\n", + adapter->curbssparams.channel, adapter->adhocchannel); + + setcurrentchannel(priv, adapter->adhocchannel); + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel != adapter->adhocchannel) { + lbs_pr_debug(1, "failed to updated channel to %d, channel = %d\n", + adapter->adhocchannel, adapter->curbssparams.channel); + LEAVE(); + return -1; + } + + if (adapter->connect_status == libertas_connected) { + int i; + struct WLAN_802_11_SSID curadhocssid; + + lbs_pr_debug(1, "channel Changed while in an IBSS\n"); + + /* Copy the current ssid */ + memcpy(&curadhocssid, &adapter->curbssparams.ssid, + sizeof(struct WLAN_802_11_SSID)); + + /* Exit Adhoc mode */ + lbs_pr_debug(1, "In changeadhocchannel(): Sending Adhoc Stop\n"); + ret = libertas_stop_adhoc_network(priv); + + if (ret) { + LEAVE(); + return ret; + } + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &curadhocssid, 0); + + // find out the BSSID that matches the current SSID + i = libertas_find_SSID_in_list(adapter, &curadhocssid, NULL, + IW_MODE_ADHOC); + + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List," + "so join\n", i); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + // else send START command + lbs_pr_debug(1, "SSID not found in list, " + "so creating adhoc with ssid = %s\n", + curadhocssid.ssid); + libertas_start_adhoc_network(priv, &curadhocssid); + } // end of else (START command) + } + + LEAVE(); + return 0; +} + +/** + * @brief Set Radio On/OFF + * + * @param priv A pointer to wlan_private structure + * @option Radio Option + * @return 0 --success, otherwise fail + */ +int wlan_radio_ioctl(wlan_private * priv, u8 option) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->radioon != option) { + lbs_pr_debug(1, "Switching %s the Radio\n", option ? "On" : "Off"); + adapter->radioon = option; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + } + + LEAVE(); + return ret; +} + +/** + * @brief Copy rates + * + * @param dest A pointer to Dest Buf + * @param src A pointer to Src Buf + * @param len The len of Src Buf + * @return Number of rates copyed + */ +static inline int copyrates(u8 * dest, int pos, u8 * src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= sizeof(u8) * WLAN_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/** + * @brief Get active data rates + * + * @param adapter A pointer to wlan_adapter structure + * @param rate The buf to return the active rates + * @return The number of rates + */ +static int get_active_data_rates(wlan_adapter * adapter, + u8* rates) +{ + int k = 0; + + ENTER(); + + if (adapter->connect_status != libertas_connected) { + if (adapter->mode == IW_MODE_INFRA) { + lbs_pr_debug(1, "Infra\n"); + k = copyrates(rates, k, libertas_supported_rates, + sizeof(libertas_supported_rates)); + } else { + lbs_pr_debug(1, "Adhoc G\n"); + k = copyrates(rates, k, libertas_adhoc_rates_g, + sizeof(libertas_adhoc_rates_g)); + } + } else { + k = copyrates(rates, 0, adapter->curbssparams.datarates, + adapter->curbssparams.numofrates); + } + + LEAVE(); + + return k; +} + +static int wlan_get_name(struct net_device *dev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + const char *cp; + char comm[6] = { "COMM-" }; + char mrvl[6] = { "MRVL-" }; + int cnt; + + ENTER(); + + strcpy(cwrq, mrvl); + + cp = strstr(libertas_driver_version, comm); + if (cp == libertas_driver_version) //skip leading "COMM-" + cp = libertas_driver_version + strlen(comm); + else + cp = libertas_driver_version; + + cnt = strlen(mrvl); + cwrq += cnt; + while (cnt < 16 && (*cp != '-')) { + *cwrq++ = toupper(*cp++); + cnt++; + } + *cwrq = '\0'; + + LEAVE(); + + return 0; +} + +static int wlan_get_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct chan_freq_power *cfp; + + ENTER(); + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, + adapter->curbssparams.channel); + + if (!cfp) { + if (adapter->curbssparams.channel) + lbs_pr_debug(1, "Invalid channel=%d\n", + adapter->curbssparams.channel); + return -EINVAL; + } + + fwrq->m = (long)cfp->freq * 100000; + fwrq->e = 1; + + lbs_pr_debug(1, "freq=%u\n", fwrq->m); + + LEAVE(); + return 0; +} + +static int wlan_get_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->connect_status == libertas_connected) { + memcpy(awrq->sa_data, adapter->curbssparams.bssid, ETH_ALEN); + } else { + memset(awrq->sa_data, 0, ETH_ALEN); + } + awrq->sa_family = ARPHRD_ETHER; + + LEAVE(); + return 0; +} + +static int wlan_set_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Check the size of the string + */ + + if (dwrq->length > 16) { + return -E2BIG; + } + + mutex_lock(&adapter->lock); + memset(adapter->nodename, 0, sizeof(adapter->nodename)); + memcpy(adapter->nodename, extra, dwrq->length); + mutex_unlock(&adapter->lock); + + LEAVE(); + return 0; +} + +static int wlan_get_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Get the Nick Name saved + */ + + mutex_lock(&adapter->lock); + strncpy(extra, adapter->nodename, 16); + mutex_unlock(&adapter->lock); + + extra[16] = '\0'; + + /* + * If none, we may want to get the one that was set + */ + + /* + * Push it out ! + */ + dwrq->length = strlen(extra) + 1; + + LEAVE(); + return 0; +} + +static int wlan_set_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + adapter->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE; + } else { + if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE) + return -EINVAL; + adapter->rtsthsd = rthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, &rthr); + + LEAVE(); + return ret; +} + +static int wlan_get_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->rtsthsd = 0; + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->rtsthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE) + || (vwrq->value > MRVDRV_RTS_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return 0; +} + +static int wlan_set_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + int fthr = vwrq->value; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->disabled) { + adapter->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE; + } else { + if (fthr < MRVDRV_FRAG_MIN_VALUE + || fthr > MRVDRV_FRAG_MAX_VALUE) + return -EINVAL; + adapter->fragthsd = fthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, &fthr); + LEAVE(); + return ret; +} + +static int wlan_get_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->fragthsd = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->fragthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE) + || (vwrq->value > MRVDRV_FRAG_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return ret; +} + +static int wlan_get_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + *uwrq = adapter->mode; + + LEAVE(); + return 0; +} + +static int wlan_get_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_get, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "TXPOWER GET %d dbm.\n", adapter->txpowerlevel); + vwrq->value = adapter->txpowerlevel; + vwrq->fixed = 1; + if (adapter->radioon) { + vwrq->disabled = 0; + vwrq->flags = IW_TXPOW_DBM; + } else { + vwrq->disabled = 1; + } + + LEAVE(); + return 0; +} + +static int wlan_set_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->flags == IW_RETRY_LIMIT) { + /* The MAC has a 4-bit Total_Tx_Count register + Total_Tx_Count = 1 + Tx_Retry_Count */ +#define TX_RETRY_MIN 0 +#define TX_RETRY_MAX 14 + if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX) + return -EINVAL; + + /* Adding 1 to convert retry count to try count */ + adapter->txretrycount = vwrq->value + 1; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } else { + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + +static int wlan_get_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + adapter->txretrycount = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + if (ret) { + LEAVE(); + return ret; + } + vwrq->disabled = 0; + if (!vwrq->flags) { + vwrq->flags = IW_RETRY_LIMIT; + /* Subtract 1 to convert try count to retry count */ + vwrq->value = adapter->txretrycount - 1; + } + + LEAVE(); + return 0; +} + +static inline void sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/* data rate listing + MULTI_BANDS: + abg a b b/g + Infra G(12) A(8) B(4) G(12) + Adhoc A+B(12) A(8) B(4) B(4) + + non-MULTI_BANDS: + b b/g + Infra B(4) G(12) + Adhoc B(4) B(4) + */ +/** + * @brief Get Range Info + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_range(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int i, j; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_range *range = (struct iw_range *)extra; + struct chan_freq_power *cfp; + u8 rates[WLAN_SUPPORTED_RATES]; + + u8 flag = 0; + + ENTER(); + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + memset(rates, 0, sizeof(rates)); + range->num_bitrates = get_active_data_rates(adapter, rates); + + for (i = 0; i < min_t(__u8, range->num_bitrates, IW_MAX_BITRATES) && rates[i]; + i++) { + range->bitrate[i] = (rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + lbs_pr_debug(1, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, + range->num_bitrates); + + range->num_frequency = 0; + if (priv->adapter->enable11d && + adapter->connect_status == libertas_connected) { + u8 chan_no; + u8 band; + + struct parsed_region_chan_11d *parsed_region_chan = + &adapter->parsed_region_chan; + + if (parsed_region_chan == NULL) { + lbs_pr_debug(1, "11D:parsed_region_chan is NULL\n"); + LEAVE(); + return 0; + } + band = parsed_region_chan->band; + lbs_pr_debug(1, "band=%d NoOfChan=%d\n", band, + parsed_region_chan->nr_chan); + + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (i < parsed_region_chan->nr_chan); i++) { + chan_no = parsed_region_chan->chanpwr[i].chan; + lbs_pr_debug(1, "chan_no=%d\n", chan_no); + range->freq[range->num_frequency].i = (long)chan_no; + range->freq[range->num_frequency].m = + (long)libertas_chan_2_freq(chan_no, band) * 100000; + range->freq[range->num_frequency].e = 1; + range->num_frequency++; + } + flag = 1; + } + if (!flag) { + for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (j < sizeof(adapter->region_channel) + / sizeof(adapter->region_channel[0])); j++) { + cfp = adapter->region_channel[j].CFP; + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && adapter->region_channel[j].valid + && cfp + && (i < adapter->region_channel[j].nrcfp); i++) { + range->freq[range->num_frequency].i = + (long)cfp->channel; + range->freq[range->num_frequency].m = + (long)cfp->freq * 100000; + range->freq[range->num_frequency].e = 1; + cfp++; + range->num_frequency++; + } + } + } + + lbs_pr_debug(1, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MRVDRV_RTS_MIN_VALUE; + range->max_rts = MRVDRV_RTS_MAX_VALUE; + range->min_frag = MRVDRV_FRAG_MIN_VALUE; + range->max_frag = MRVDRV_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + + range->min_pmp = 1000000; + range->max_pmp = 120000000; + range->min_pmt = 1000; + range->max_pmt = 1000000; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = TX_RETRY_MIN; + range->max_retry = TX_RETRY_MAX; + + /* + * Set the qual, level and noise range values + */ + range->max_qual.qual = 100; + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->avg_qual.qual = 70; + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->sensitivity = 0; + + /* + * Setup the supported power level ranges + */ + memset(range->txpower, 0, sizeof(range->txpower)); + range->txpower[0] = 5; + range->txpower[1] = 7; + range->txpower[2] = 9; + range->txpower[3] = 11; + range->txpower[4] = 13; + range->txpower[5] = 15; + range->txpower[6] = 17; + range->txpower[7] = 19; + + range->num_txpower = 8; + range->txpower_capa = IW_TXPOW_DBM; + range->txpower_capa |= IW_TXPOW_RANGE; + + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + if (adapter->fwcapinfo & FW_CAPINFO_WPA) { + range->enc_capa = IW_ENC_CAPA_WPA + | IW_ENC_CAPA_WPA2 + | IW_ENC_CAPA_CIPHER_TKIP + | IW_ENC_CAPA_CIPHER_CCMP; + } + + LEAVE(); + return 0; +} + +static int wlan_set_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + if (vwrq->disabled) { + adapter->psmode = wlan802_11powermodecam; + if (adapter->psstate != PS_STATE_FULL_POWER) { + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + return 0; + } + + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + lbs_pr_debug(1, + "Setting power timeout command is not supported\n"); + return -EINVAL; + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { + lbs_pr_debug(1, "Setting power period command is not supported\n"); + return -EINVAL; + } + + if (adapter->psmode != wlan802_11powermodecam) { + return 0; + } + + adapter->psmode = wlan802_11powermodemax_psp; + + if (adapter->connect_status == libertas_connected) { + libertas_ps_sleep(priv, cmd_option_waitforrsp); + } + + LEAVE(); + return 0; +} + +static int wlan_get_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int mode; + + ENTER(); + + mode = adapter->psmode; + + if ((vwrq->disabled = (mode == wlan802_11powermodecam)) + || adapter->connect_status == libertas_disconnected) { + LEAVE(); + return 0; + } + + vwrq->value = 0; + + LEAVE(); + return 0; +} + +/* + * iwpriv settable callbacks + */ + +static const iw_handler wlan_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; + +static const struct iw_priv_args wlan_private_args[] = { + /* + * { cmd, set_args, get_args, name } + */ + /* Using iwpriv sub-command feature */ + { + WLAN_SETONEINT_GETNONE, /* IOCTL: 24 */ + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + ""}, + { + WLANSETREGION, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setregioncode"}, + { + WLAN_SUBCMD_MESH_SET_TTL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "mesh_set_ttl"}, + { + WLAN_SETNONE_GETONEINT, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + { + WLANGETREGION, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getregioncode"}, + { + WLAN_SUBCMD_FWT_CLEANUP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_cleanup"}, + { + WLAN_SUBCMD_FWT_TIME, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_time"}, + { + WLAN_SUBCMD_MESH_GET_TTL, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "mesh_get_ttl"}, + { + WLAN_SETNONE_GETNONE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + ""}, + { + WLAN_SUBCMD_FWT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "fwt_reset"}, + { + WLAN_SUBCMD_BT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "bt_reset"}, + { + WLAN_SET128CHAR_GET128CHAR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + ""}, + /* BT Management */ + { + WLAN_SUBCMD_BT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_add"}, + { + WLAN_SUBCMD_BT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_del"}, + { + WLAN_SUBCMD_BT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_list"}, + /* FWT Management */ + { + WLAN_SUBCMD_FWT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_add"}, + { + WLAN_SUBCMD_FWT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_del"}, + { + WLAN_SUBCMD_FWT_LOOKUP, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_lookup"}, + { + WLAN_SUBCMD_FWT_LIST_NEIGHBOR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_neigh"}, + { + WLAN_SUBCMD_FWT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list"}, + { + WLAN_SUBCMD_FWT_LIST_ROUTE, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_route"}, + { + WLAN_SET_GET_SIXTEEN_INT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + ""}, + { + WLAN_LED_GPIO_CTRL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "ledgpio"}, +}; + +static struct iw_statistics *wlan_get_wireless_stats(struct net_device *dev) +{ + enum { + POOR = 30, + FAIR = 60, + GOOD = 80, + VERY_GOOD = 90, + EXCELLENT = 95, + PERFECT = 100 + }; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 rssi_qual; + u32 tx_qual; + u32 quality = 0; + int stats_valid = 0; + u8 rssi; + u32 tx_retries; + + ENTER(); + + priv->wstats.status = adapter->mode; + + /* If we're not associated, all quality values are meaningless */ + if (adapter->connect_status != libertas_connected) + goto out; + + /* Quality by RSSI */ + priv->wstats.qual.level = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + priv->wstats.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + + lbs_pr_debug(1, "Signal Level = %#x\n", priv->wstats.qual.level); + lbs_pr_debug(1, "Noise = %#x\n", priv->wstats.qual.noise); + + rssi = priv->wstats.qual.level - priv->wstats.qual.noise; + if (rssi < 15) + rssi_qual = rssi * POOR / 10; + else if (rssi < 20) + rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR; + else if (rssi < 30) + rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR; + else if (rssi < 40) + rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) / + 10 + GOOD; + else + rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) / + 10 + VERY_GOOD; + quality = rssi_qual; + + /* Quality by TX errors */ + priv->wstats.discard.retries = priv->stats.tx_errors; + + tx_retries = adapter->logmsg.retry; + + if (tx_retries > 75) + tx_qual = (90 - tx_retries) * POOR / 15; + else if (tx_retries > 70) + tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; + else if (tx_retries > 65) + tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; + else if (tx_retries > 50) + tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / + 15 + GOOD; + else + tx_qual = (50 - tx_retries) * + (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; + quality = min(quality, tx_qual); + + priv->wstats.discard.code = adapter->logmsg.wepundecryptable; + priv->wstats.discard.fragment = adapter->logmsg.fcserror; + priv->wstats.discard.retries = tx_retries; + priv->wstats.discard.misc = adapter->logmsg.ackfailure; + + /* Calculate quality */ + priv->wstats.qual.qual = max(quality, (u32)100); + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + stats_valid = 1; + + /* update stats asynchronously for future calls */ + libertas_prepare_and_send_command(priv, cmd_802_11_rssi, 0, + 0, 0, NULL); + libertas_prepare_and_send_command(priv, cmd_802_11_get_log, 0, + 0, 0, NULL); +out: + if (!stats_valid) { + priv->wstats.miss.beacon = 0; + priv->wstats.discard.retries = 0; + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED; + priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + } + + LEAVE (); + return &priv->wstats; + + +} + +static int wlan_set_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rc = -EINPROGRESS; /* Call commit handler */ + struct chan_freq_power *cfp; + + ENTER(); + + /* + * If setting by frequency, convert to a channel + */ + if (fwrq->e == 1) { + + long f = fwrq->m / 100000; + int c = 0; + + cfp = find_cfp_by_band_and_freq(adapter, 0, f); + if (!cfp) { + lbs_pr_debug(1, "Invalid freq=%ld\n", f); + return -EINVAL; + } + + c = (int)cfp->channel; + + if (c < 0) + return -EINVAL; + + fwrq->e = 0; + fwrq->m = c; + } + + /* + * Setting by channel number + */ + if (fwrq->m > 1000 || fwrq->e > 0) { + rc = -EOPNOTSUPP; + } else { + int channel = fwrq->m; + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, channel); + if (!cfp) { + rc = -EINVAL; + } else { + if (adapter->mode == IW_MODE_ADHOC) { + rc = changeadhocchannel(priv, channel); + /* If station is WEP enabled, send the + * command to set WEP in firmware + */ + if (adapter->secinfo.wep_enabled) { + lbs_pr_debug(1, "set_freq: WEP enabled\n"); + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, + NULL); + + if (ret) { + LEAVE(); + return ret; + } + + adapter->currentpacketfilter |= + cmd_act_mac_wep_enable; + + libertas_set_mac_packet_filter(priv); + } + } else { + rc = -EOPNOTSUPP; + } + } + } + + LEAVE(); + return rc; +} + +/** + * @brief use index to get the data rate + * + * @param index The index of data rate + * @return data rate or 0 + */ +u32 libertas_index_to_data_rate(u8 index) +{ + if (index >= sizeof(libertas_wlan_data_rates)) + index = 0; + + return libertas_wlan_data_rates[index]; +} + +/** + * @brief use rate to get the index + * + * @param rate data rate + * @return index or 0 + */ +u8 libertas_data_rate_to_index(u32 rate) +{ + u8 *ptr; + + if (rate) + if ((ptr = memchr(libertas_wlan_data_rates, (u8) rate, + sizeof(libertas_wlan_data_rates)))) + return (ptr - libertas_wlan_data_rates); + + return 0; +} + +static int wlan_set_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 data_rate; + u16 action; + int ret = 0; + u8 rates[WLAN_SUPPORTED_RATES]; + u8 *rate; + + ENTER(); + + lbs_pr_debug(1, "Vwrq->value = %d\n", vwrq->value); + + if (vwrq->value == -1) { + action = cmd_act_set_tx_auto; // Auto + adapter->is_datarate_auto = 1; + adapter->datarate = 0; + } else { + if (vwrq->value % 100000) { + return -EINVAL; + } + + data_rate = vwrq->value / 500000; + + memset(rates, 0, sizeof(rates)); + get_active_data_rates(adapter, rates); + rate = rates; + while (*rate) { + lbs_pr_debug(1, "Rate=0x%X Wanted=0x%X\n", *rate, + data_rate); + if ((*rate & 0x7f) == (data_rate & 0x7f)) + break; + rate++; + } + if (!*rate) { + lbs_pr_alert( "The fixed data rate 0x%X is out " + "of range.\n", data_rate); + return -EINVAL; + } + + adapter->datarate = data_rate; + action = cmd_act_set_tx_fix_rate; + adapter->is_datarate_auto = 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + action, cmd_option_waitforrsp, 0, NULL); + + LEAVE(); + return ret; +} + +static int wlan_get_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->is_datarate_auto) { + vwrq->fixed = 0; + } else { + vwrq->fixed = 1; + } + + vwrq->value = adapter->datarate * 500000; + + LEAVE(); + return 0; +} + +static int wlan_set_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + + ENTER(); + + if ( (*uwrq != IW_MODE_ADHOC) + && (*uwrq != IW_MODE_INFRA) + && (*uwrq != IW_MODE_AUTO)) { + lbs_pr_debug(1, "Invalid mode: 0x%x\n", *uwrq); + ret = -EINVAL; + goto out; + } + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + wlan_cancel_association_work(priv); + } else { + assoc_req->mode = *uwrq; + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + wlan_postpone_association_work(priv); + lbs_pr_debug(1, "Switching to mode: 0x%x\n", *uwrq); + } + mutex_unlock(&adapter->lock); + +out: + LEAVE(); + return ret; +} + + +/** + * @brief Get Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, u8 * extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + ENTER(); + + lbs_pr_debug(1, "flags=0x%x index=%d length=%d wep_tx_keyidx=%d\n", + dwrq->flags, index, dwrq->length, adapter->wep_tx_keyidx); + + dwrq->flags = 0; + + /* Authentication method */ + switch (adapter->secinfo.auth_mode) { + case IW_AUTH_ALG_OPEN_SYSTEM: + dwrq->flags = IW_ENCODE_OPEN; + break; + + case IW_AUTH_ALG_SHARED_KEY: + case IW_AUTH_ALG_LEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + + if ( adapter->secinfo.wep_enabled + || adapter->secinfo.WPAenabled + || adapter->secinfo.WPA2enabled) { + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + memset(extra, 0, 16); + + mutex_lock(&adapter->lock); + + /* Default to returning current transmit key */ + if (index < 0) + index = adapter->wep_tx_keyidx; + + if ((adapter->wep_keys[index].len) && adapter->secinfo.wep_enabled) { + memcpy(extra, adapter->wep_keys[index].key, + adapter->wep_keys[index].len); + dwrq->length = adapter->wep_keys[index].len; + + dwrq->flags |= (index + 1); + /* Return WEP enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else if ((adapter->secinfo.WPAenabled) + || (adapter->secinfo.WPA2enabled)) { + /* return WPA enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + mutex_unlock(&adapter->lock); + + dwrq->flags |= IW_ENCODE_NOKEY; + + lbs_pr_debug(1, "key:%02x:%02x:%02x:%02x:%02x:%02x keylen=%d\n", + extra[0], extra[1], extra[2], + extra[3], extra[4], extra[5], dwrq->length); + + lbs_pr_debug(1, "Return flags=0x%x\n", dwrq->flags); + + LEAVE(); + return 0; +} + +/** + * @brief Set Encryption key (internal) + * + * @param priv A pointer to private card structure + * @param key_material A pointer to key material + * @param key_length length of key material + * @param index key index to set + * @param set_tx_key Force set TX key (1 = yes, 0 = no) + * @return 0 --success, otherwise fail + */ +static int wlan_set_wep_key(struct assoc_request *assoc_req, + const char *key_material, + u16 key_length, + u16 index, + int set_tx_key) +{ + struct WLAN_802_11_KEY *pkey; + + ENTER(); + + /* Paranoid validation of key index */ + if (index > 3) { + LEAVE(); + return -EINVAL; + } + + /* validate max key length */ + if (key_length > KEY_LEN_WEP_104) { + LEAVE(); + return -EINVAL; + } + + pkey = &assoc_req->wep_keys[index]; + + if (key_length > 0) { + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + pkey->type = KEY_TYPE_ID_WEP; + + /* Standardize the key length */ + pkey->len = (key_length > KEY_LEN_WEP_40) ? + KEY_LEN_WEP_104 : KEY_LEN_WEP_40; + memcpy(pkey->key, key_material, key_length); + } + + if (set_tx_key) { + /* Ensure the chosen key is valid */ + if (!pkey->len) { + lbs_pr_debug(1, "key not set, so cannot enable it\n"); + LEAVE(); + return -EINVAL; + } + assoc_req->wep_tx_keyidx = index; + } + + assoc_req->secinfo.wep_enabled = 1; + + LEAVE(); + return 0; +} + +static int validate_key_index(u16 def_index, u16 raw_index, + u16 *out_index, u16 *is_default) +{ + if (!out_index || !is_default) + return -EINVAL; + + /* Verify index if present, otherwise use default TX key index */ + if (raw_index > 0) { + if (raw_index > 4) + return -EINVAL; + *out_index = raw_index - 1; + } else { + *out_index = def_index; + *is_default = 1; + } + return 0; +} + +static void disable_wep(struct assoc_request *assoc_req) +{ + int i; + + /* Set Open System auth mode */ + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + + /* Clear WEP keys and mark WEP as disabled */ + assoc_req->secinfo.wep_enabled = 0; + for (i = 0; i < 4; i++) + assoc_req->wep_keys[i].len = 0; + + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); +} + +/** + * @brief Set Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + u16 is_default = 0, index = 0, set_tx_key = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->flags & IW_ENCODE_DISABLED) { + disable_wep (assoc_req); + goto out; + } + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) { + ret = -EINVAL; + goto out; + } + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, set the TX key. + */ + if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default)) + set_tx_key = 1; + + ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key); + if (ret) + goto out; + + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Get Extended Encryption key (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 on success, otherwise failure + */ +static int wlan_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = -EINVAL; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int index, max_key_len; + + ENTER(); + + max_key_len = dwrq->length - sizeof(*ext); + if (max_key_len < 0) + goto out; + + index = dwrq->flags & IW_ENCODE_INDEX; + if (index) { + if (index < 1 || index > 4) + goto out; + index--; + } else { + index = adapter->wep_tx_keyidx; + } + + if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY && + ext->alg != IW_ENCODE_ALG_WEP) { + if (index != 0 || adapter->mode != IW_MODE_INFRA) + goto out; + } + + dwrq->flags = index + 1; + memset(ext, 0, sizeof(*ext)); + + if ( !adapter->secinfo.wep_enabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + dwrq->flags |= IW_ENCODE_DISABLED; + } else { + u8 *key = NULL; + + if ( adapter->secinfo.wep_enabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_WEP; + ext->key_len = adapter->wep_keys[index].len; + key = &adapter->wep_keys[index].key[0]; + } else if ( !adapter->secinfo.wep_enabled + && (adapter->secinfo.WPAenabled || + adapter->secinfo.WPA2enabled)) { + /* WPA */ + ext->alg = IW_ENCODE_ALG_TKIP; + ext->key_len = 0; + } else { + goto out; + } + + if (ext->key_len > max_key_len) { + ret = -E2BIG; + goto out; + } + + if (ext->key_len) + memcpy(ext->key, key, ext->key_len); + else + dwrq->flags |= IW_ENCODE_NOKEY; + dwrq->flags |= IW_ENCODE_ENABLED; + } + ret = 0; + +out: + LEAVE(); + return ret; +} + +/** + * @brief Set Encryption key Extended (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int alg = ext->alg; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) { + disable_wep (assoc_req); + } else if (alg == IW_ENCODE_ALG_WEP) { + u16 is_default = 0, index, set_tx_key = 0; + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) + goto out; + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, or if the set-TX-key flag was passed, set the TX key. + */ + if ( !assoc_req->secinfo.wep_enabled + || (dwrq->length == 0 && !is_default) + || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) + set_tx_key = 1; + + /* Copy key to driver */ + ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index, + set_tx_key); + if (ret) + goto out; + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + } + + /* Mark the various WEP bits as modified */ + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) { + struct WLAN_802_11_KEY * pkey; + + /* validate key length */ + if (((alg == IW_ENCODE_ALG_TKIP) + && (ext->key_len != KEY_LEN_WPA_TKIP)) + || ((alg == IW_ENCODE_ALG_CCMP) + && (ext->key_len != KEY_LEN_WPA_AES))) { + lbs_pr_debug(1, "Invalid size %d for key of alg" + "type %d.\n", + ext->key_len, + alg); + ret = -EINVAL; + goto out; + } + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) + pkey = &assoc_req->wpa_mcast_key; + else + pkey = &assoc_req->wpa_unicast_key; + + memset(pkey, 0, sizeof (struct WLAN_802_11_KEY)); + memcpy(pkey->key, ext->key, ext->key_len); + pkey->len = ext->key_len; + pkey->flags = KEY_INFO_WPA_ENABLED; + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { + pkey->flags |= KEY_INFO_WPA_MCAST; + set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); + } else { + pkey->flags |= KEY_INFO_WPA_UNICAST; + set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); + } + + if (alg == IW_ENCODE_ALG_TKIP) + pkey->type = KEY_TYPE_ID_TKIP; + else if (alg == IW_ENCODE_ALG_CCMP) + pkey->type = KEY_TYPE_ID_AES; + + /* If WPA isn't enabled yet, do that now */ + if ( assoc_req->secinfo.WPAenabled == 0 + && assoc_req->secinfo.WPA2enabled == 0) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + } + + disable_wep (assoc_req); + } + +out: + if (ret == 0) { + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + + +static int wlan_set_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->length > MAX_WPA_IE_LEN || + (dwrq->length && extra == NULL)) { + ret = -EINVAL; + goto out; + } + + if (dwrq->length) { + memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length); + assoc_req->wpa_ie_len = dwrq->length; + } else { + memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->wpa_ie)); + assoc_req->wpa_ie_len = 0; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->wpa_ie_len == 0) { + dwrq->length = 0; + LEAVE(); + return 0; + } + + if (dwrq->length < adapter->wpa_ie_len) { + LEAVE(); + return -E2BIG; + } + + dwrq->length = adapter->wpa_ie_len; + memcpy(extra, &adapter->wpa_ie[0], adapter->wpa_ie_len); + + LEAVE(); + return 0; +} + + +static int wlan_set_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + int updated = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * libertas does not use these parameters + */ + break; + + case IW_AUTH_WPA_VERSION: + if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.wep_enabled = 0; + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) { + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.wep_enabled = 0; + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + } + updated = 1; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + if (dwrq->value) { + adapter->currentpacketfilter |= + cmd_act_mac_strict_protection_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_strict_protection_enable; + } + updated = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) { + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; + } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) { + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + } else if (dwrq->value & IW_AUTH_ALG_LEAP) { + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP; + } else { + ret = -EINVAL; + } + updated = 1; + break; + + case IW_AUTH_WPA_ENABLED: + if (dwrq->value) { + if (!assoc_req->secinfo.WPAenabled && + !assoc_req->secinfo.WPA2enabled) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.wep_enabled = 0; + assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + } + } else { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + updated = 1; + break; + + default: + ret = -EOPNOTSUPP; + break; + } + +out: + if (ret == 0) { + if (updated) + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else if (ret != -EOPNOTSUPP) { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + dwrq->value = 0; + if (adapter->secinfo.WPAenabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA; + if (adapter->secinfo.WPA2enabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA2; + if (!dwrq->value) + dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dwrq->value = 0; + if (adapter->currentpacketfilter & + cmd_act_mac_strict_protection_enable) + dwrq->value = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + dwrq->value = adapter->secinfo.auth_mode; + break; + + case IW_AUTH_WPA_ENABLED: + if (adapter->secinfo.WPAenabled && adapter->secinfo.WPA2enabled) + dwrq->value = 1; + break; + + default: + LEAVE(); + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + + +static int wlan_set_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + u16 dbm; + + ENTER(); + + if (vwrq->disabled) { + wlan_radio_ioctl(priv, RADIO_OFF); + return 0; + } + + adapter->preamble = cmd_type_auto_preamble; + + wlan_radio_ioctl(priv, RADIO_ON); + + if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) { + dbm = (u16) mw_to_dbm(vwrq->value); + } else + dbm = (u16) vwrq->value; + + /* auto tx power control */ + + if (vwrq->fixed == 0) + dbm = 0xffff; + + lbs_pr_debug(1, "<1>TXPOWER SET %d dbm.\n", dbm); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_set_low, + cmd_option_waitforrsp, 0, (void *)&dbm); + + LEAVE(); + return ret; +} + +static int wlan_get_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + /* + * Note : if dwrq->flags != 0, we should get the relevant SSID from + * the SSID list... + */ + + /* + * Get the current SSID + */ + if (adapter->connect_status == libertas_connected) { + memcpy(extra, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } else { + memset(extra, 0, 32); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } + /* + * If none, we may want to get the one that was set + */ + + /* To make the driver backward compatible with WPA supplicant v0.2.4 */ + if (dwrq->length == 32) /* check with WPA supplicant buffer size */ + dwrq->length = min_t(size_t, adapter->curbssparams.ssid.ssidlength, + IW_ESSID_MAX_SIZE); + else + dwrq->length = adapter->curbssparams.ssid.ssidlength + 1; + + dwrq->flags = 1; /* active */ + + LEAVE(); + return 0; +} + +static int wlan_set_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct WLAN_802_11_SSID ssid; + struct assoc_request * assoc_req; + int ssid_len = dwrq->length; + + ENTER(); + + /* + * WE-20 and earlier NULL pad the end of the SSID and increment + * SSID length so it can be used like a string. WE-21 and later don't, + * but some userspace tools aren't able to cope with the change. + */ + if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0')) + ssid_len--; + + /* Check the size of the string */ + if (ssid_len > IW_ESSID_MAX_SIZE) { + ret = -E2BIG; + goto out; + } + + memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID)); + + if (!dwrq->flags || !ssid_len) { + /* "any" SSID requested; leave SSID blank */ + } else { + /* Specific SSID requested */ + memcpy(&ssid.ssid, extra, ssid_len); + ssid.ssidlength = ssid_len; + } + + lbs_pr_debug(1, "Requested new SSID = %s\n", + (ssid.ssidlength > 0) ? (char *)ssid.ssid : "any"); + +out: + mutex_lock(&adapter->lock); + if (ret == 0) { + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + } else { + /* Copy the SSID to the association request */ + memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID)); + set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + } + + /* Cancel the association request if there was an error */ + if (ret != 0) { + wlan_cancel_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Connect to the AP or Ad-hoc Network with specific bssid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + + lbs_pr_debug(1, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data)); + + mutex_lock(&adapter->lock); + + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + wlan_cancel_association_work(priv); + ret = -ENOMEM; + } else { + /* Copy the BSSID to the association request */ + memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN); + set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + return ret; +} + +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen) +{ + union { + u32 l; + u8 c[4]; + } ver; + char fwver[32]; + + mutex_lock(&adapter->lock); + ver.l = adapter->fwreleasenumber; + mutex_unlock(&adapter->lock); + + if (ver.c[3] == 0) + sprintf(fwver, "%u.%u.%u", ver.c[2], ver.c[1], ver.c[0]); + else + sprintf(fwver, "%u.%u.%u.p%u", + ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + + snprintf(fwversion, maxlen, fwver); +} + + +/* + * iwconfig settable callbacks + */ +static const iw_handler wlan_handler[] = { + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) wlan_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) wlan_set_freq, /* SIOCSIWFREQ */ + (iw_handler) wlan_get_freq, /* SIOCGIWFREQ */ + (iw_handler) wlan_set_mode, /* SIOCSIWMODE */ + (iw_handler) wlan_get_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) wlan_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) wlan_set_wap, /* SIOCSIWAP */ + (iw_handler) wlan_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* SIOCSIWMLME */ + (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */ + (iw_handler) libertas_set_scan, /* SIOCSIWSCAN */ + (iw_handler) libertas_get_scan, /* SIOCGIWSCAN */ + (iw_handler) wlan_set_essid, /* SIOCSIWESSID */ + (iw_handler) wlan_get_essid, /* SIOCGIWESSID */ + (iw_handler) wlan_set_nick, /* SIOCSIWNICKN */ + (iw_handler) wlan_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_rate, /* SIOCSIWRATE */ + (iw_handler) wlan_get_rate, /* SIOCGIWRATE */ + (iw_handler) wlan_set_rts, /* SIOCSIWRTS */ + (iw_handler) wlan_get_rts, /* SIOCGIWRTS */ + (iw_handler) wlan_set_frag, /* SIOCSIWFRAG */ + (iw_handler) wlan_get_frag, /* SIOCGIWFRAG */ + (iw_handler) wlan_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) wlan_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) wlan_set_retry, /* SIOCSIWRETRY */ + (iw_handler) wlan_get_retry, /* SIOCGIWRETRY */ + (iw_handler) wlan_set_encode, /* SIOCSIWENCODE */ + (iw_handler) wlan_get_encode, /* SIOCGIWENCODE */ + (iw_handler) wlan_set_power, /* SIOCSIWPOWER */ + (iw_handler) wlan_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_genie, /* SIOCSIWGENIE */ + (iw_handler) wlan_get_genie, /* SIOCGIWGENIE */ + (iw_handler) wlan_set_auth, /* SIOCSIWAUTH */ + (iw_handler) wlan_get_auth, /* SIOCGIWAUTH */ + (iw_handler) wlan_set_encodeext,/* SIOCSIWENCODEEXT */ + (iw_handler) wlan_get_encodeext,/* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ +}; + +struct iw_handler_def libertas_handler_def = { + .num_standard = sizeof(wlan_handler) / sizeof(iw_handler), + .num_private = sizeof(wlan_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(wlan_private_args) / + sizeof(struct iw_priv_args), + .standard = (iw_handler *) wlan_handler, + .private = (iw_handler *) wlan_private_handler, + .private_args = (struct iw_priv_args *)wlan_private_args, + .get_wireless_stats = wlan_get_wireless_stats, +}; diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h new file mode 100644 index 00000000000..15cfaaf0797 --- /dev/null +++ b/drivers/net/wireless/libertas/wext.h @@ -0,0 +1,62 @@ +/** + * This file contains definition for IOCTL call. + */ +#ifndef _WLAN_WEXT_H_ +#define _WLAN_WEXT_H_ + +#define SUBCMD_OFFSET 4 +#define SUBCMD_DATA(x) *((int *)(x->u.name + SUBCMD_OFFSET)) + +/** PRIVATE CMD ID */ +#define WLANIOCTL SIOCIWFIRSTPRIV + +#define WLAN_SETNONE_GETNONE (WLANIOCTL + 8) +#define WLAN_SUBCMD_BT_RESET 13 +#define WLAN_SUBCMD_FWT_RESET 14 + +#define WLAN_SETNONE_GETONEINT (WLANIOCTL + 15) +#define WLANGETREGION 1 + +#define WLAN_SUBCMD_FWT_CLEANUP 15 +#define WLAN_SUBCMD_FWT_TIME 16 +#define WLAN_SUBCMD_MESH_GET_TTL 17 + +#define WLAN_SETONEINT_GETNONE (WLANIOCTL + 24) +#define WLANSETREGION 8 +#define WLAN_SUBCMD_MESH_SET_TTL 18 + +#define WLAN_SET128CHAR_GET128CHAR (WLANIOCTL + 25) +#define WLAN_SUBCMD_BT_ADD 18 +#define WLAN_SUBCMD_BT_DEL 19 +#define WLAN_SUBCMD_BT_LIST 20 +#define WLAN_SUBCMD_FWT_ADD 21 +#define WLAN_SUBCMD_FWT_DEL 22 +#define WLAN_SUBCMD_FWT_LOOKUP 23 +#define WLAN_SUBCMD_FWT_LIST_NEIGHBOR 24 +#define WLAN_SUBCMD_FWT_LIST 25 +#define WLAN_SUBCMD_FWT_LIST_ROUTE 26 + +#define WLAN_SET_GET_SIXTEEN_INT (WLANIOCTL + 29) +#define WLAN_LED_GPIO_CTRL 5 + +#define WLAN_LINKMODE_802_3 0 +#define WLAN_LINKMODE_802_11 2 +#define WLAN_RADIOMODE_NONE 0 +#define WLAN_RADIOMODE_RADIOTAP 2 + +/** wlan_ioctl_regrdwr */ +struct wlan_ioctl_regrdwr { + /** Which register to access */ + u16 whichreg; + /** Read or Write */ + u16 action; + u32 offset; + u16 NOB; + u32 value; +}; + +extern struct iw_handler_def libertas_handler_def; +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int i); +int wlan_radio_ioctl(wlan_private * priv, u8 option); + +#endif /* _WLAN_WEXT_H_ */ |